diff --git a/controller/src/main/java/org/jboss/as/controller/ResourceRegistration.java b/controller/src/main/java/org/jboss/as/controller/ResourceRegistration.java index e1b00838aa8..e773ce7d302 100644 --- a/controller/src/main/java/org/jboss/as/controller/ResourceRegistration.java +++ b/controller/src/main/java/org/jboss/as/controller/ResourceRegistration.java @@ -5,6 +5,8 @@ package org.jboss.as.controller; +import java.util.Objects; + import org.jboss.as.version.Stability; /** @@ -32,12 +34,7 @@ static ResourceRegistration root() { * @return a resource registration */ static ResourceRegistration of(PathElement path) { - return new ResourceRegistration() { - @Override - public PathElement getPathElement() { - return path; - } - }; + return (path != null) ? new DefaultResourceRegistration(path) : DefaultResourceRegistration.ROOT; } /** @@ -47,16 +44,42 @@ public PathElement getPathElement() { * @return a resource registration */ static ResourceRegistration of(PathElement path, Stability stability) { - return new ResourceRegistration() { - @Override - public PathElement getPathElement() { - return path; - } - + return (path != null) || (stability != DefaultResourceRegistration.ROOT.getStability()) ? new DefaultResourceRegistration(path) { @Override public Stability getStability() { return stability; } - }; + } : DefaultResourceRegistration.ROOT; + } + + class DefaultResourceRegistration implements ResourceRegistration { + static final ResourceRegistration ROOT = new DefaultResourceRegistration(null); + private final PathElement path; + + DefaultResourceRegistration(PathElement path) { + this.path = path; + } + + @Override + public PathElement getPathElement() { + return this.path; + } + + @Override + public int hashCode() { + return Objects.hashCode(this.path); + } + + @Override + public boolean equals(Object object) { + if (!(object instanceof ResourceRegistration)) return false; + ResourceRegistration registration = (ResourceRegistration) object; + return Objects.equals(this.getPathElement(), registration.getPathElement()) && Objects.equals(this.getStability(), registration.getStability()); + } + + @Override + public String toString() { + return Objects.toString(this.path); + } } } diff --git a/controller/src/test/java/org/jboss/as/controller/ResourceRegistrationTestCase.java b/controller/src/test/java/org/jboss/as/controller/ResourceRegistrationTestCase.java new file mode 100644 index 00000000000..03e2ba4c1c2 --- /dev/null +++ b/controller/src/test/java/org/jboss/as/controller/ResourceRegistrationTestCase.java @@ -0,0 +1,50 @@ +/* + * Copyright The WildFly Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.jboss.as.controller; + +import java.util.EnumSet; +import java.util.Objects; + +import org.jboss.as.controller.descriptions.ModelDescriptionConstants; +import org.jboss.as.version.Stability; +import org.junit.Assert; +import org.junit.Test; + +/** + * Unit test for {@link ResourceRegistration} factory methods. + */ +public class ResourceRegistrationTestCase { + + @Test + public void test() { + ResourceRegistration root = ResourceRegistration.root(); + Assert.assertNull(root.getPathElement()); + Assert.assertSame(Stability.DEFAULT, root.getStability()); + Assert.assertSame(root, ResourceRegistration.root()); + Assert.assertSame(root, ResourceRegistration.of(root.getPathElement())); + Assert.assertSame(root, ResourceRegistration.of(root.getPathElement(), Stability.DEFAULT)); + verify(root); + + ResourceRegistration subsystem = ResourceRegistration.of(PathElement.pathElement(ModelDescriptionConstants.SUBSYSTEM, "foo")); + Assert.assertNotEquals(subsystem, root); + Assert.assertNotEquals(subsystem, ResourceRegistration.of(PathElement.pathElement(ModelDescriptionConstants.SUBSYSTEM, "bar"))); + verify(subsystem); + + ResourceRegistration unstableSubsystem = ResourceRegistration.of(PathElement.pathElement(ModelDescriptionConstants.SUBSYSTEM, "bar"), Stability.EXPERIMENTAL); + Assert.assertNotEquals(unstableSubsystem, root); + Assert.assertNotEquals(unstableSubsystem, subsystem); + Assert.assertNotEquals(unstableSubsystem, ResourceRegistration.of(unstableSubsystem.getPathElement())); + verify(unstableSubsystem); + } + + private static void verify(ResourceRegistration subject) { + Assert.assertEquals(Objects.hashCode(subject.getPathElement()), subject.hashCode()); + Assert.assertEquals(subject, ResourceRegistration.of(subject.getPathElement(), subject.getStability())); + for (Stability stability : EnumSet.complementOf(EnumSet.of(subject.getStability()))) { + Assert.assertNotEquals(subject, ResourceRegistration.of(subject.getPathElement(), stability)); + } + } +} diff --git a/subsystem/src/main/java/org/wildfly/subsystem/resource/PathElementProvider.java b/subsystem/src/main/java/org/wildfly/subsystem/resource/PathElementProvider.java index 9b0a3f1db08..60719d46fe8 100644 --- a/subsystem/src/main/java/org/wildfly/subsystem/resource/PathElementProvider.java +++ b/subsystem/src/main/java/org/wildfly/subsystem/resource/PathElementProvider.java @@ -11,7 +11,9 @@ /** * Provides a {@link PathElement}. * @author Paul Ferraro + * @deprecated To be removed without replacement */ +@Deprecated(forRemoval = true, since = "26.0.0") public interface PathElementProvider extends Supplier { /** diff --git a/subsystem/src/main/java/org/wildfly/subsystem/resource/ResourceDescriptor.java b/subsystem/src/main/java/org/wildfly/subsystem/resource/ResourceDescriptor.java index af26c104793..67a19d18fde 100644 --- a/subsystem/src/main/java/org/wildfly/subsystem/resource/ResourceDescriptor.java +++ b/subsystem/src/main/java/org/wildfly/subsystem/resource/ResourceDescriptor.java @@ -14,6 +14,7 @@ import java.util.Set; import java.util.function.BiPredicate; import java.util.function.Consumer; +import java.util.function.Function; import java.util.function.Supplier; import java.util.function.UnaryOperator; import java.util.stream.Collectors; @@ -23,6 +24,7 @@ import org.jboss.as.controller.OperationContext; import org.jboss.as.controller.OperationStepHandler; import org.jboss.as.controller.PathElement; +import org.jboss.as.controller.ResourceRegistration; import org.jboss.as.controller.capability.RuntimeCapability; import org.jboss.as.controller.descriptions.ModelDescriptionConstants; import org.jboss.as.controller.descriptions.ResourceDescriptionResolver; @@ -159,8 +161,8 @@ class DefaultResourceDescriptor implements ResourceDescriptor { private final Map, BiPredicate> capabilities; private final Map readWriteAttributes = new HashMap<>(); private final Iterable readOnlyAttributes; - private final Set requiredChildren; - private final Set requiredSingletonChildren; + private final Map requiredChildren; + private final Map requiredSingletonChildren; private final Map attributeTranslations; private final Set> resourceCapabilityReferences; private final Map> operationTransformers; @@ -254,12 +256,12 @@ public OperationStepHandler getWriteAttributeOperationStepHandler(AttributeDefin } @Override - public Set getRequiredChildren() { + public Map getRequiredChildResources() { return this.requiredChildren; } @Override - public Set getRequiredSingletonChildren() { + public Map getRequiredSingletonChildResources() { return this.requiredSingletonChildren; } @@ -395,9 +397,31 @@ default C addCapabilities(Collection> capabilities) { * Defines a required child of this resource. Required children will be automatically added, if no child resource exists with the specified path. * @param path the path of the required child resource * @return a reference to this configurator + * @deprecated Superseded by {@link #requireChildResource(ResourceRegistration)}. */ + @Deprecated(forRemoval = true, since = "26.0.0") default C requireChild(PathElement path) { - return this.requireChildren(Set.of(path)); + return this.requireChildResources(Set.of(ResourceRegistration.of(path))); + } + + /** + * Defines a set of required children of this resource. Required children will be automatically added, if no child resource exists with the specified path. + * @param paths a set of paths of the required child resources + * @return a reference to this configurator + * @deprecated Superseded by {@link #requireChildResources(Set)}. + */ + @Deprecated(forRemoval = true, since = "26.0.0") + default C requireChildren(Set paths) { + return this.requireChildResources(paths.stream().map(ResourceRegistration::of).collect(Collectors.toUnmodifiableSet())); + } + + /** + * Defines a required child of this resource. Required children will be automatically added, if no child resource exists with the specified path. + * @param path the path of the required child resource + * @return a reference to this configurator + */ + default C requireChildResource(ResourceRegistration child) { + return this.requireChildResources(Set.of(child)); } /** @@ -405,15 +429,37 @@ default C requireChild(PathElement path) { * @param paths a set of paths of the required child resources * @return a reference to this configurator */ - C requireChildren(Set paths); + C requireChildResources(Set children); /** * Defines a required singleton child of this resource. Required singleton children will be automatically added, if no child resource exists with the same path key. * @param path the path of the required singleton child resource * @return a reference to this configurator + * @deprecated Superseded by {@link #requireSingletonChildResource(ResourceRegistration)}. */ + @Deprecated(forRemoval = true, since = "26.0.0") default C requireSingletonChild(PathElement path) { - return this.requireSingletonChildren(Set.of(path)); + return this.requireSingletonChildResources(Set.of(ResourceRegistration.of(path))); + } + + /** + * Defines a set of required singleton children of this resource. Required singleton children will be automatically added, if no child resource exists with the same path key. + * @param paths a set of paths of the required singleton child resources + * @return a reference to this configurator + * @deprecated Superseded by {@link #requireSingletonChildResources(Set)}. + */ + @Deprecated(forRemoval = true, since = "26.0.0") + default C requireSingletonChild(Set paths) { + return this.requireSingletonChildResources(paths.stream().map(ResourceRegistration::of).collect(Collectors.toUnmodifiableSet())); + } + + /** + * Defines a required singleton child of this resource. Required singleton children will be automatically added, if no child resource exists with the same path key. + * @param path the path of the required singleton child resource + * @return a reference to this configurator + */ + default C requireSingletonChildResource(ResourceRegistration path) { + return this.requireSingletonChildResources(Set.of(path)); } /** @@ -421,7 +467,7 @@ default C requireSingletonChild(PathElement path) { * @param paths a set of paths of the required singleton child resources * @return a reference to this configurator */ - C requireSingletonChildren(Set paths); + C requireSingletonChildResources(Set paths); /** * Adds a capability reference that records a requirement for this resource. @@ -529,15 +575,23 @@ default

>> C provideCapabilities(Collect * Defines a set of required children of this resource. Required children will be automatically added, if no child resource exists with the specified path. * @param providers a set of providers of the required child resource paths * @return a reference to this configurator + * @deprecated Use {@link #requireChildResources(Set)} instead. */ -

> C provideRequiredChildren(Collection

providers); + @Deprecated(forRemoval = true, since = "26.0.0") + default

> C provideRequiredChildren(Collection

providers) { + return this.requireChildResources(providers.stream().map(Supplier::get).map(ResourceRegistration::of).collect(Collectors.toUnmodifiableSet())); + } /** * Defines a set of required singleton children of this resource. Required singleton children will be automatically added, if no child resource exists with the same path key. * @param providers a set of providers of the required singleton child resource paths * @return a reference to this configurator + * @deprecated Use {@link #requireSingletonChildResources(Set)} instead. */ -

> C provideRequiredSingletonChildren(Collection

providers); + @Deprecated(forRemoval = true, since = "26.0.0") + default

> C provideRequiredSingletonChildren(Collection

providers) { + return this.requireSingletonChildResources(providers.stream().map(Supplier::get).map(ResourceRegistration::of).collect(Collectors.toUnmodifiableSet())); + } } /** @@ -569,8 +623,8 @@ abstract static class AbstractConfigurator> implements private Collection modelOnlyAttributes = List.of(); private Collection readOnlyAttributes = List.of(); private Map customAttributes = Map.of(); - private Set requiredChildren = Set.of(); - private Set requiredSingletonChildren = Set.of(); + private Map requiredChildren = Map.of(); + private Map requiredSingletonChildren = Map.of(); private Map attributeTranslations = Map.of(); private Set> resourceCapabilityReferences = Set.of(); private Map> operationTransformers = Map.of(); @@ -655,14 +709,14 @@ public C addCapabilities(Collection> capabilities, BiPredic } @Override - public C requireChildren(Set paths) { - this.requiredChildren = this.requiredChildren.isEmpty() ? Set.copyOf(paths) : concat(this.requiredChildren, paths.stream()); + public C requireChildResources(Set children) { + this.requiredChildren = this.requiredChildren.isEmpty() ? children.stream().collect(Collectors.toMap(ResourceRegistration::getPathElement, Function.identity())) : concat(this.requiredChildren, children.stream().map(child -> Map.entry(child.getPathElement(), child))); return this.self(); } @Override - public C requireSingletonChildren(Set paths) { - this.requiredSingletonChildren = this.requiredSingletonChildren.isEmpty() ? Set.copyOf(paths) : concat(this.requiredSingletonChildren, paths.stream()); + public C requireSingletonChildResources(Set children) { + this.requiredSingletonChildren = this.requiredSingletonChildren.isEmpty() ? children.stream().collect(Collectors.toMap(ResourceRegistration::getPathElement, Function.identity())) : concat(this.requiredSingletonChildren, children.stream().map(child -> Map.entry(child.getPathElement(), child))); return this.self(); } @@ -727,18 +781,6 @@ public

>> C provideCapabilities(Collecti return this.self(); } - @Override - public

> C provideRequiredChildren(Collection

providers) { - this.requiredChildren = concat(this.requiredChildren, stream(providers)); - return this.self(); - } - - @Override - public

>C provideRequiredSingletonChildren(Collection

providers) { - this.requiredSingletonChildren = concat(this.requiredSingletonChildren, stream(providers)); - return this.self(); - } - private static Collection copyOf(Collection collection) { // Create defensive copy, if collection was not already immutable return (collection instanceof Set) ? Set.copyOf((Set) collection) : List.copyOf(collection); diff --git a/subsystem/src/main/java/org/wildfly/subsystem/resource/operation/AddResourceOperationStepHandler.java b/subsystem/src/main/java/org/wildfly/subsystem/resource/operation/AddResourceOperationStepHandler.java index 120f3109ee7..e38e69655a0 100644 --- a/subsystem/src/main/java/org/wildfly/subsystem/resource/operation/AddResourceOperationStepHandler.java +++ b/subsystem/src/main/java/org/wildfly/subsystem/resource/operation/AddResourceOperationStepHandler.java @@ -20,6 +20,7 @@ import org.jboss.as.controller.OperationStepHandler; import org.jboss.as.controller.PathAddress; import org.jboss.as.controller.PathElement; +import org.jboss.as.controller.ResourceRegistration; import org.jboss.as.controller.capability.RuntimeCapability; import org.jboss.as.controller.descriptions.ModelDescriptionConstants; import org.jboss.as.controller.logging.ControllerLogger; @@ -66,7 +67,7 @@ public void execute(OperationContext context, ModelNode operation) throws Operat @SuppressWarnings("unchecked") AddResourceOperationStepHandlerDescriptor parentDescriptor = ((DescribedOperationStepHandler) parentHandler).getDescriptor(); - if (parentDescriptor.getRequiredChildren().contains(path)) { + if (parentDescriptor.getRequiredChildResources().containsKey(path)) { if (context.readResourceFromRoot(parentAddress, false).hasChild(path)) { // If we are a required child resource of our parent, we need to remove the auto-created resource first context.addStep(Util.createRemoveOperation(address), context.getRootResourceRegistration().getOperationHandler(address, ModelDescriptionConstants.REMOVE), OperationContext.Stage.MODEL); @@ -74,7 +75,7 @@ public void execute(OperationContext context, ModelNode operation) throws Operat return; } } - for (PathElement requiredPath : parentDescriptor.getRequiredSingletonChildren()) { + for (PathElement requiredPath : parentDescriptor.getRequiredSingletonChildResources().keySet()) { String requiredPathKey = requiredPath.getKey(); if (requiredPath.getKey().equals(path.getKey())) { Set childrenNames = context.readResourceFromRoot(parentAddress, false).getChildrenNames(requiredPathKey); @@ -183,8 +184,8 @@ protected void populateModel(OperationContext context, ModelNode operation, Reso } // Auto-create required child resources as necessary - addRequiredChildren(context, this.descriptor.getRequiredChildren(), Resource::hasChild); - addRequiredChildren(context, this.descriptor.getRequiredSingletonChildren(), AddResourceOperationStepHandler::hasChildren); + addRequiredChildren(context, this.descriptor.getRequiredChildResources().values(), Resource::hasChild); + addRequiredChildren(context, this.descriptor.getRequiredSingletonChildResources().values(), AddResourceOperationStepHandler::hasChildren); // Don't leave model undefined if (!model.isDefined()) { @@ -196,7 +197,7 @@ private static boolean hasChildren(Resource resource, PathElement path) { return resource.hasChildren(path.getKey()); } - private static void addRequiredChildren(OperationContext context, Collection paths, BiPredicate present) { + private static void addRequiredChildren(OperationContext context, Collection childResources, BiPredicate present) { OperationStepHandler addIfAbsentHandler = new OperationStepHandler() { @Override public void execute(OperationContext context, ModelNode operation) throws OperationFailedException { @@ -208,8 +209,10 @@ public void execute(OperationContext context, ModelNode operation) throws Operat } } }; - for (PathElement path : paths) { - context.addStep(Util.createAddOperation(context.getCurrentAddress().append(path)), addIfAbsentHandler, OperationContext.Stage.MODEL); + for (ResourceRegistration childResource : childResources) { + if (context.enables(childResource)) { + context.addStep(Util.createAddOperation(context.getCurrentAddress().append(childResource.getPathElement())), addIfAbsentHandler, OperationContext.Stage.MODEL); + } } } diff --git a/subsystem/src/main/java/org/wildfly/subsystem/resource/operation/AddResourceOperationStepHandlerDescriptor.java b/subsystem/src/main/java/org/wildfly/subsystem/resource/operation/AddResourceOperationStepHandlerDescriptor.java index 9ff2c4ee86f..b8587e5596f 100644 --- a/subsystem/src/main/java/org/wildfly/subsystem/resource/operation/AddResourceOperationStepHandlerDescriptor.java +++ b/subsystem/src/main/java/org/wildfly/subsystem/resource/operation/AddResourceOperationStepHandlerDescriptor.java @@ -4,6 +4,7 @@ */ package org.wildfly.subsystem.resource.operation; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.function.Consumer; @@ -11,6 +12,7 @@ import org.jboss.as.controller.AttributeDefinition; import org.jboss.as.controller.PathElement; +import org.jboss.as.controller.ResourceRegistration; import org.jboss.as.controller.registry.Resource; import org.jboss.as.server.DeploymentProcessorTarget; import org.wildfly.subsystem.resource.AttributeTranslation; @@ -24,18 +26,39 @@ public interface AddResourceOperationStepHandlerDescriptor extends OperationStep /** * Returns the required child resources for this resource description. * @return a collection of resource paths + * @deprecated Superseded by {@link #getRequiredChildResources()}. */ + @Deprecated(forRemoval = true, since = "26.0.0") default Set getRequiredChildren() { - return Set.of(); + return this.getRequiredChildResources().keySet(); + } + + /** + * Returns the required child resources for this resource description. + * @return a collection of resource paths + */ + default Map getRequiredChildResources() { + return Map.of(); } /** * Returns the required singleton child resources for this resource description. * This means only one child resource should exist for the given child type. * @return a collection of resource paths + * @deprecated Superseded by {@link #getRequiredSingletonChildResources() */ + @Deprecated(forRemoval = true, since = "26.0.0") default Set getRequiredSingletonChildren() { - return Set.of(); + return this.getRequiredSingletonChildResources().keySet(); + } + + /** + * Returns the required singleton child resources for this resource description. + * This means only one child resource should exist for the given child type. + * @return a collection of resource paths + */ + default Map getRequiredSingletonChildResources() { + return Map.of(); } /**