<commit_before>/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2010, 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.operations.global;

import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ACCESS_TYPE;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ATTRIBUTES;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.CHILDREN;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.CHILD_TYPE;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DEFAULT;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.FAILURE_DESCRIPTION;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.INCLUDE_DEFAULTS;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.INCLUDE_RUNTIME;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.INHERITED;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.LOCALE;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.MODEL_DESCRIPTION;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.NAME;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OPERATIONS;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PROXIES;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_ATTRIBUTE_OPERATION;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_ONLY;
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.RESTART_REQUIRED;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RESULT;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.STORAGE;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

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.descriptions.DescriptionProvider;
import org.jboss.as.controller.operations.validation.ModelTypeValidator;
import org.jboss.as.controller.operations.validation.ParametersValidator;
import org.jboss.as.controller.operations.validation.StringLengthValidator;
import org.jboss.as.controller.registry.AttributeAccess;
import org.jboss.as.controller.registry.AttributeAccess.AccessType;
import org.jboss.as.controller.registry.AttributeAccess.Storage;
import org.jboss.as.controller.registry.ImmutableManagementResourceRegistration;
import org.jboss.as.controller.registry.OperationEntry;
import org.jboss.as.controller.registry.Resource;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.ModelType;

/**
 * Global {@code OperationHandler}s.
 *
 * @author <a href="<EMAIL>"><NAME></a>
 */
public class GlobalOperationHandlers {

    public static final OperationStepHandler READ_RESOURCE = new ReadResourceHandler();
    public static final OperationStepHandler READ_ATTRIBUTE = new ReadAttributeHandler();
    public static final OperationStepHandler READ_CHILDREN_NAMES = new ReadChildrenNamesOperationHandler();
    public static final OperationStepHandler READ_CHILDREN_RESOURCES = new ReadChildrenResourcesOperationHandler();
    public static final OperationStepHandler WRITE_ATTRIBUTE = new WriteAttributeHandler();
    public static final OperationStepHandler VALIDATE_ADDRESS = new OperationStepHandler() {

        @Override
        public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
            try {
                context.readResource(PathAddress.EMPTY_ADDRESS);
            } catch (Exception e) {
                context.getFailureDescription().set(new ModelNode().set("resource does not exist: " + operation.get(OP_ADDR)));
            }
            context.completeStep();
        }
    };


    public static final String VALIDATE_ADDRESS_OPERATION_NAME = "validate-address";

    private GlobalOperationHandlers() {
        //
    }

    /**
     * {@link org.jboss.as.controller.OperationStepHandler} reading a part of the model. The result will only contain the current attributes of a node by default,
     * excluding all addressable children and runtime attributes. Setting the request parameter "recursive" to "true" will recursively include
     * all children and configuration attributes. Non-recursive queries can include runtime attributes by setting the request parameter
     * "include-runtime" to "true".
     */
    public static class ReadResourceHandler extends AbstractMultiTargetHandler implements OperationStepHandler {

        private final ParametersValidator validator = new ParametersValidator();

        public ReadResourceHandler() {
            validator.registerValidator(RECURSIVE, new ModelTypeValidator(ModelType.BOOLEAN, true));
            validator.registerValidator(INCLUDE_RUNTIME, new ModelTypeValidator(ModelType.BOOLEAN, true));
            validator.registerValidator(PROXIES, new ModelTypeValidator(ModelType.BOOLEAN, true));
            validator.registerValidator(INCLUDE_DEFAULTS, new ModelTypeValidator(ModelType.BOOLEAN, true));
        }

        @Override
        public void doExecute(OperationContext context, ModelNode operation) throws OperationFailedException {

            validator.validate(operation);

            final String opName = operation.require(OP).asString();
            final ModelNode opAddr = operation.get(OP_ADDR);
            final PathAddress address = PathAddress.pathAddress(opAddr);
            final boolean recursive = operation.get(RECURSIVE).asBoolean(false);
            final boolean queryRuntime = !recursive && operation.get(INCLUDE_RUNTIME).asBoolean(false);
            final boolean proxies = operation.get(PROXIES).asBoolean(false);
            final boolean defaults = operation.get(INCLUDE_DEFAULTS).asBoolean(true);

            // Attributes read directly from the model with no special read handler step in the middle
            final Map<String, ModelNode> directAttributes = new HashMap<String, ModelNode>();
            // Children names read directly from the model with no special read handler step in the middle
            final Map<String, ModelNode> directChildren = new HashMap<String, ModelNode>();
            // Attributes of AccessType.METRIC
            final Map<String, ModelNode> metrics = queryRuntime ? new HashMap<String, ModelNode>() : Collections.<String, ModelNode>emptyMap();
            // Non-AccessType.METRIC attributes with a special read handler registered
            final Map<String, ModelNode> otherAttributes = new HashMap<String, ModelNode>();
            // Child resources recursively read
            final Map<PathElement, ModelNode> childResources = recursive ? new HashMap<PathElement, ModelNode>() : Collections.<PathElement, ModelNode>emptyMap();

            // We're going to add a bunch of steps that should immediately follow this one. We are going to add them
            // in reverse order of how they should execute, as that is the way adding a Stage.IMMEDIATE step works

            // Last to execute is the handler that assembles the overall response from the pieces created by all the other steps
            final ReadResourceAssemblyHandler assemblyHandler = new ReadResourceAssemblyHandler(directAttributes, metrics, otherAttributes, directChildren, childResources);
            context.addStep(assemblyHandler, queryRuntime ? OperationContext.Stage.VERIFY : OperationContext.Stage.IMMEDIATE);
            final ImmutableManagementResourceRegistration registry = context.getResourceRegistration();
            final Resource resource = context.readResource(PathAddress.EMPTY_ADDRESS);
            // Get the model for this resource.
            final ModelNode model = resource.getModel();


            final Map<String, Set<String>> childrenByType = registry != null ? getChildAddresses(registry, resource, null) : Collections.<String, Set<String>>emptyMap();

            if (model.isDefined()) {
                // Store direct attributes first
                for (String key : model.keys()) {
                    // In case someone put some garbage in it
                    if (!childrenByType.containsKey(key)) {
                        directAttributes.put(key, model.get(key));
                    }
                }
                if (defaults) {
                    //get the model description
                    final DescriptionProvider descriptionProvider = registry.getModelDescription(PathAddress.EMPTY_ADDRESS);
                    final Locale locale = getLocale(operation);
                    final ModelNode nodeDescription = descriptionProvider.getModelDescription(locale);

                    if (nodeDescription.isDefined() && nodeDescription.hasDefined(ATTRIBUTES)) {
                        for (String key : nodeDescription.get(ATTRIBUTES).keys()) {
                            if ((!childrenByType.containsKey(key)) &&
                                    (!directAttributes.containsKey(key) || !directAttributes.get(key).isDefined()) &&
                                    nodeDescription.get(ATTRIBUTES).hasDefined(key) &&
                                    nodeDescription.get(ATTRIBUTES, key).hasDefined(DEFAULT)) {
                                directAttributes.put(key, nodeDescription.get(ATTRIBUTES, key, DEFAULT));
                            }
                        }
                    }
                }
            }

            // Next, process child resources
            for (Map.Entry<String, Set<String>> entry : childrenByType.entrySet()) {
                String childType = entry.getKey();
                Set<String> children = entry.getValue();
                if (children.isEmpty()) {
                    // Just treat it like an undefined attribute
                    directAttributes.put(childType, new ModelNode());
                } else {
                    for (String child : children) {
                        boolean storeDirect = !recursive;
                        if (recursive) {
                            PathElement childPE = PathElement.pathElement(childType, child);
                            PathAddress relativeAddr = PathAddress.pathAddress(childPE);
                            ImmutableManagementResourceRegistration childReg = registry.getSubModel(relativeAddr);
                            if (childReg == null) {
                                throw new OperationFailedException(new ModelNode().set(String.format("no child registry for (%s, %s)", childType, child)));
                            }
                            // We only invoke runtime resources if they are remote proxies
                            if (childReg.isRuntimeOnly() && (!proxies || !childReg.isRemote())) {
                                storeDirect = true;
                            } else {
                                // Add a step to read the child resource
                                ModelNode rrOp = new ModelNode();
                                rrOp.get(OP).set(opName);
                                rrOp.get(OP_ADDR).set(PathAddress.pathAddress(address, childPE).toModelNode());
                                rrOp.get(RECURSIVE).set(true);
                                rrOp.get(PROXIES).set(proxies);
                                rrOp.get(INCLUDE_RUNTIME).set(queryRuntime);
                                ModelNode rrRsp = new ModelNode();
                                childResources.put(childPE, rrRsp);

                                OperationStepHandler rrHandler = childReg.getOperationHandler(relativeAddr, opName);
                                context.addStep(rrRsp, rrOp, rrHandler, OperationContext.Stage.IMMEDIATE);
                            }
                        }
                        if (storeDirect) {
                            ModelNode childMap = directChildren.get(childType);
                            if (childMap == null) {
                                childMap = new ModelNode();
                                childMap.setEmptyObject();
                                directChildren.put(childType, childMap);
                            }
                            // Add a "child" => undefined
                            childMap.get(child);
                        }
                    }
                }
            }

            // Last, handle attributes with read handlers registered
            final Set<String> attributeNames = registry != null ? registry.getAttributeNames(PathAddress.EMPTY_ADDRESS) : Collections.<String>emptySet();
            for (final String attributeName : attributeNames) {
                final AttributeAccess access = registry.getAttributeAccess(PathAddress.EMPTY_ADDRESS, attributeName);
                if (access == null) {
                    continue;
                } else {
                    final AttributeAccess.Storage storage = access.getStorageType();
                    if (!queryRuntime && storage != AttributeAccess.Storage.CONFIGURATION) {
                        continue;
                    }
                    final AccessType type = access.getAccessType();
                    final OperationStepHandler handler = access.getReadHandler();
                    if (handler != null) {
                        // Discard any directAttribute map entry for this, as the read handler takes precedence
                        directAttributes.remove(attributeName);
                        // Create the attribute operation
                        final ModelNode attributeOperation = new ModelNode();
                        attributeOperation.get(OP_ADDR).set(opAddr);
                        attributeOperation.get(OP).set(READ_ATTRIBUTE_OPERATION);
                        attributeOperation.get(NAME).set(attributeName);

                        final ModelNode attrResponse = new ModelNode();
                        if (type == AccessType.METRIC) {
                            metrics.put(attributeName, attrResponse);
                        } else {
                            otherAttributes.put(attributeName, attrResponse);
                        }
                        context.addStep(attrResponse, attributeOperation, handler, OperationContext.Stage.IMMEDIATE);
                    }
                }
            }
            context.completeStep();
        }
    }

    ;

    /**
     * Assembles the response to a read-resource request from the components gathered by earlier steps.
     */
    private static class ReadResourceAssemblyHandler implements OperationStepHandler {

        private final Map<String, ModelNode> directAttributes;
        private final Map<String, ModelNode> directChildren;
        private final Map<String, ModelNode> metrics;
        private final Map<String, ModelNode> otherAttributes;
        private final Map<PathElement, ModelNode> childResources;

        /**
         * Creates a ReadResourceAssemblyHandler that will assemble the response using the contents
         * of the given maps.
         *
         * @param directAttributes
         * @param metrics          map of attributes of AccessType.METRIC. Keys are the attribute names, values are the full
         *                         read-attribute response from invoking the attribute's read handler. Will not be {@code null}
         * @param otherAttributes  map of attributes not of AccessType.METRIC that have a read handler registered. Keys
         *                         are the attribute names, values are the full read-attribute response from invoking the
         *                         attribute's read handler. Will not be {@code null}
         * @param directChildren
         * @param childResources   read-resource response from child resources, where the key is the PathAddress
         *                         relative to the address of the operation this handler is handling and the
         *                         value is the full read-resource response. Will not be {@code null}
         */
        private ReadResourceAssemblyHandler(final Map<String, ModelNode> directAttributes, final Map<String, ModelNode> metrics,
                                            final Map<String, ModelNode> otherAttributes, Map<String, ModelNode> directChildren, final Map<PathElement, ModelNode> childResources) {
            this.directAttributes = directAttributes;
            this.metrics = metrics;
            this.otherAttributes = otherAttributes;
            this.directChildren = directChildren;
            this.childResources = childResources;
        }

        @Override
        public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {

            Map<String, ModelNode> sortedAttributes = new TreeMap<String, ModelNode>();
            Map<String, ModelNode> sortedChildren = new TreeMap<String, ModelNode>();
            boolean failed = false;
            for (Map.Entry<String, ModelNode> entry : otherAttributes.entrySet()) {
                ModelNode value = entry.getValue();
                if (!value.has(FAILURE_DESCRIPTION)) {
                    sortedAttributes.put(entry.getKey(), value.get(RESULT));
                } else if (!failed && value.hasDefined(FAILURE_DESCRIPTION)) {
                    context.getFailureDescription().set(value.get(FAILURE_DESCRIPTION));
                    failed = true;
                    break;
                }
            }
            if (!failed) {
                for (Map.Entry<PathElement, ModelNode> entry : childResources.entrySet()) {
                    PathElement path = entry.getKey();
                    ModelNode value = entry.getValue();
                    if (!value.has(FAILURE_DESCRIPTION)) {
                        ModelNode childTypeNode = sortedChildren.get(path.getKey());
                        if (childTypeNode == null) {
                            childTypeNode = new ModelNode();
                            sortedChildren.put(path.getKey(), childTypeNode);
                        }
                        childTypeNode.get(path.getValue()).set(value.get(RESULT));
                    } else if (!failed && value.hasDefined(FAILURE_DESCRIPTION)) {
                        context.getFailureDescription().set(value.get(FAILURE_DESCRIPTION));
                        failed = true;
                    }
                }
            }
            if (!failed) {
                for (Map.Entry<String, ModelNode> simpleAttribute : directAttributes.entrySet()) {
                    sortedAttributes.put(simpleAttribute.getKey(), simpleAttribute.getValue());
                }
                for (Map.Entry<String, ModelNode> directChild : directChildren.entrySet()) {
                    sortedChildren.put(directChild.getKey(), directChild.getValue());
                }
                for (Map.Entry<String, ModelNode> metric : metrics.entrySet()) {
                    ModelNode value = metric.getValue();
                    if (!value.has(FAILURE_DESCRIPTION)) {
                        sortedAttributes.put(metric.getKey(), value.get(RESULT));
                    }
                    // we ignore metric failures
                    // TODO how to prevent the metric failure screwing up the overall context?
                }

                final ModelNode result = context.getResult();
                result.setEmptyObject();
                for (Map.Entry<String, ModelNode> entry : sortedAttributes.entrySet()) {
                    result.get(entry.getKey()).set(entry.getValue());
                }

                for (Map.Entry<String, ModelNode> entry : sortedChildren.entrySet()) {
                    result.get(entry.getKey()).set(entry.getValue());
                }
            }

            context.completeStep();
        }
    }

    /**
     * {@link org.jboss.as.controller.OperationStepHandler} reading a single attribute at the given operation address. The required request parameter "name" represents the attribute name.
     */
    public static class ReadAttributeHandler extends AbstractMultiTargetHandler implements OperationStepHandler {

        private ParametersValidator validator = new ParametersValidator();

        public ReadAttributeHandler() {
            validator.registerValidator(NAME, new StringLengthValidator(1));
            validator.registerValidator(INCLUDE_DEFAULTS, new ModelTypeValidator(ModelType.BOOLEAN, true));
        }

        @Override
        public void doExecute(OperationContext context, ModelNode operation) throws OperationFailedException {
            validator.validate(operation);
            final String attributeName = operation.require(NAME).asString();
            final boolean defaults = operation.get(INCLUDE_DEFAULTS).asBoolean(true);

            final ModelNode subModel = safeReadModel(context);
            final ImmutableManagementResourceRegistration registry = context.getResourceRegistration();
            final AttributeAccess attributeAccess = registry.getAttributeAccess(PathAddress.EMPTY_ADDRESS, attributeName);


            if (attributeAccess == null) {
                final Set<String> children = context.getResourceRegistration().getChildNames(PathAddress.EMPTY_ADDRESS);
                if (children.contains(attributeName)) {
                    throw new OperationFailedException(new ModelNode().set(String.format("'%s' is a registered child of resource (%s)", attributeName, operation.get(OP_ADDR)))); // TODO i18n
                } else if (subModel.hasDefined(attributeName)) {
                    final ModelNode result = subModel.get(attributeName);
                    context.getResult().set(result);
                } else {
                    // No defined value in the model. See if we should reply with a default from the metadata,
                    // reply with undefined, or fail because it's a non-existent attribute name
                    final ModelNode nodeDescription = getNodeDescription(registry, operation);
                    if (defaults && nodeDescription.get(ATTRIBUTES).hasDefined(attributeName) &&
                            nodeDescription.get(ATTRIBUTES, attributeName).hasDefined(DEFAULT)) {
                        final ModelNode result = nodeDescription.get(ATTRIBUTES, attributeName, DEFAULT);
                        context.getResult().set(result);
                    } else if (subModel.has(attributeName) || nodeDescription.get(ATTRIBUTES).has(attributeName)) {
                        // model had no defined value, but we treat its existence in the model or the metadata
                        // as proof that it's a legit attribute name
                        context.getResult(); // this initializes the "result" to ModelType.UNDEFINED
                    } else {
                        throw new OperationFailedException(new ModelNode().set(String.format("No known attribute %s", attributeName))); // TODO i18n
                    }
                }
                // Complete the step for the unregistered attribute case
                context.completeStep();
            } else if (attributeAccess.getReadHandler() == null) {
                // We know the attribute name is legit as it's in the registry, so this case is simpler
                if (subModel.hasDefined(attributeName) || !defaults) {
                    final ModelNode result = subModel.get(attributeName);
                    context.getResult().set(result);
                } else {
                    // It wasn't in the model, but user wants a default value from metadata if there is one
                    final ModelNode nodeDescription = getNodeDescription(registry, operation);
                    if (nodeDescription.get(ATTRIBUTES).hasDefined(attributeName) &&
                            nodeDescription.get(ATTRIBUTES, attributeName).hasDefined(DEFAULT)) {
                        final ModelNode result = nodeDescription.get(ATTRIBUTES, attributeName, DEFAULT);
                        context.getResult().set(result);
                    } else {
                        context.getResult(); // this initializes the "result" to ModelType.UNDEFINED
                    }
                }
                // Complete the step for the "registered attribute but default read handler" case
                context.completeStep();
            } else {
                OperationStepHandler handler = attributeAccess.getReadHandler();
                ClassLoader oldTccl = SecurityActions.setThreadContextClassLoader(handler.getClass());
                try {
                    handler.execute(context, operation);
                } finally {
                    SecurityActions.setThreadContextClassLoader(oldTccl);
                }
                // no context.completeStep() here as that's the read handler's job
            }
        }

        private ModelNode getNodeDescription(ImmutableManagementResourceRegistration registry, ModelNode operation) {
            final DescriptionProvider descriptionProvider = registry.getModelDescription(PathAddress.EMPTY_ADDRESS);
            final Locale locale = getLocale(operation);
            return descriptionProvider.getModelDescription(locale);
        }
    }

    ;

    /**
     * {@link org.jboss.as.controller.OperationStepHandler} writing a single attribute. The required request parameter "name" represents the attribute name.
     */
    public static class WriteAttributeHandler implements OperationStepHandler {

        private ParametersValidator nameValidator = new ParametersValidator();

        public WriteAttributeHandler() {
            nameValidator.registerValidator(NAME, new StringLengthValidator(1));
        }

        public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
            nameValidator.validate(operation);
            final String attributeName = operation.require(NAME).asString();
            final AttributeAccess attributeAccess = context.getResourceRegistration().getAttributeAccess(PathAddress.EMPTY_ADDRESS, attributeName);
            if (attributeAccess == null) {
                throw new OperationFailedException(new ModelNode().set("No known attribute called " + attributeName)); // TODO i18n
            } else if (attributeAccess.getAccessType() != AccessType.READ_WRITE) {
                throw new OperationFailedException(new ModelNode().set("Attribute " + attributeName + " is not writeable")); // TODO i18n
            } else {
                OperationStepHandler handler = attributeAccess.getWriteHandler();
                ClassLoader oldTccl = SecurityActions.setThreadContextClassLoader(handler.getClass());
                try {
                    handler.execute(context, operation);
                } finally {
                    SecurityActions.setThreadContextClassLoader(oldTccl);
                }
            }
        }
    }

    ;

    /**
     * {@link org.jboss.as.controller.OperationStepHandler} querying the children names of a given "child-type".
     */
    public static class ReadChildrenNamesOperationHandler implements OperationStepHandler {

        private final ParametersValidator validator = new ParametersValidator();

        public ReadChildrenNamesOperationHandler() {
            validator.registerValidator(CHILD_TYPE, new StringLengthValidator(1));
        }

        @Override
        public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {

            validator.validate(operation);
            final String childType = operation.require(CHILD_TYPE).asString();
            final Resource resource = context.readResource(PathAddress.EMPTY_ADDRESS);
            ImmutableManagementResourceRegistration registry = context.getResourceRegistration();
            Map<String, Set<String>> childAddresses = getChildAddresses(registry, resource, childType);
            Set<String> childNames = childAddresses.get(childType);
            if (childNames == null) {
                throw new OperationFailedException(new ModelNode().set(String.format("No known child type named %s", childType))); //TODO i18n
            }
            // Sort the result
            childNames = new TreeSet<String>(childNames);
            ModelNode result = context.getResult();
            result.setEmptyList();
            for (String childName : childNames) {
                result.add(childName);
            }

            context.completeStep();
        }
    }

    ;

    /**
     * {@link org.jboss.as.controller.OperationStepHandler} querying the children resources of a given "child-type".
     */
    public static class ReadChildrenResourcesOperationHandler implements OperationStepHandler {

        private final ParametersValidator validator = new ParametersValidator();

        public ReadChildrenResourcesOperationHandler() {
            validator.registerValidator(CHILD_TYPE, new StringLengthValidator(1));
            validator.registerValidator(RECURSIVE, new ModelTypeValidator(ModelType.BOOLEAN, true));
            validator.registerValidator(INCLUDE_RUNTIME, new ModelTypeValidator(ModelType.BOOLEAN, true));
            validator.registerValidator(PROXIES, new ModelTypeValidator(ModelType.BOOLEAN, true));
            validator.registerValidator(INCLUDE_DEFAULTS, new ModelTypeValidator(ModelType.BOOLEAN, true));
        }

        @Override
        public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {

            validator.validate(operation);
            final String childType = operation.require(CHILD_TYPE).asString();

            final Set<String> childNames = context.getResourceRegistration().getChildNames(PathAddress.EMPTY_ADDRESS);
            if (!childNames.contains(childType)) {
                throw new OperationFailedException(new ModelNode().set(String.format("No known child type named %s", childType))); //TODO i18n
            }
            final Map<PathElement, ModelNode> resources = new HashMap<PathElement, ModelNode>();

            final Resource resource = context.readResource(PathAddress.EMPTY_ADDRESS);
            if (!resource.hasChildren(childType)) {
                context.getResult().setEmptyObject();
            } else {<commit_msg>Fix messaging subsystem test issues<commit_after>/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2010, 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.operations.global;

import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ACCESS_TYPE;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ATTRIBUTES;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.CHILDREN;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.CHILD_TYPE;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DEFAULT;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.FAILURE_DESCRIPTION;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.INCLUDE_DEFAULTS;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.INCLUDE_RUNTIME;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.INHERITED;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.LOCALE;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.MODEL_DESCRIPTION;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.NAME;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OPERATIONS;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PROXIES;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_ATTRIBUTE_OPERATION;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_ONLY;
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.RESTART_REQUIRED;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RESULT;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.STORAGE;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

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.descriptions.DescriptionProvider;
import org.jboss.as.controller.operations.validation.ModelTypeValidator;
import org.jboss.as.controller.operations.validation.ParametersValidator;
import org.jboss.as.controller.operations.validation.StringLengthValidator;
import org.jboss.as.controller.registry.AttributeAccess;
import org.jboss.as.controller.registry.AttributeAccess.AccessType;
import org.jboss.as.controller.registry.AttributeAccess.Storage;
import org.jboss.as.controller.registry.ImmutableManagementResourceRegistration;
import org.jboss.as.controller.registry.OperationEntry;
import org.jboss.as.controller.registry.Resource;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.ModelType;

/**
 * Global {@code OperationHandler}s.
 *
 * @author <a href="<EMAIL>"><NAME></a>
 */
public class GlobalOperationHandlers {

    public static final OperationStepHandler READ_RESOURCE = new ReadResourceHandler();
    public static final OperationStepHandler READ_ATTRIBUTE = new ReadAttributeHandler();
    public static final OperationStepHandler READ_CHILDREN_NAMES = new ReadChildrenNamesOperationHandler();
    public static final OperationStepHandler READ_CHILDREN_RESOURCES = new ReadChildrenResourcesOperationHandler();
    public static final OperationStepHandler WRITE_ATTRIBUTE = new WriteAttributeHandler();
    public static final OperationStepHandler VALIDATE_ADDRESS = new OperationStepHandler() {

        @Override
        public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
            try {
                context.readResource(PathAddress.EMPTY_ADDRESS);
            } catch (Exception e) {
                context.getFailureDescription().set(new ModelNode().set("resource does not exist: " + operation.get(OP_ADDR)));
            }
            context.completeStep();
        }
    };


    public static final String VALIDATE_ADDRESS_OPERATION_NAME = "validate-address";

    private GlobalOperationHandlers() {
        //
    }

    /**
     * {@link org.jboss.as.controller.OperationStepHandler} reading a part of the model. The result will only contain the current attributes of a node by default,
     * excluding all addressable children and runtime attributes. Setting the request parameter "recursive" to "true" will recursively include
     * all children and configuration attributes. Non-recursive queries can include runtime attributes by setting the request parameter
     * "include-runtime" to "true".
     */
    public static class ReadResourceHandler extends AbstractMultiTargetHandler implements OperationStepHandler {

        private final ParametersValidator validator = new ParametersValidator();

        public ReadResourceHandler() {
            validator.registerValidator(RECURSIVE, new ModelTypeValidator(ModelType.BOOLEAN, true));
            validator.registerValidator(INCLUDE_RUNTIME, new ModelTypeValidator(ModelType.BOOLEAN, true));
            validator.registerValidator(PROXIES, new ModelTypeValidator(ModelType.BOOLEAN, true));
            validator.registerValidator(INCLUDE_DEFAULTS, new ModelTypeValidator(ModelType.BOOLEAN, true));
        }

        @Override
        public void doExecute(OperationContext context, ModelNode operation) throws OperationFailedException {

            validator.validate(operation);

            final String opName = operation.require(OP).asString();
            final ModelNode opAddr = operation.get(OP_ADDR);
            final PathAddress address = PathAddress.pathAddress(opAddr);
            final boolean recursive = operation.get(RECURSIVE).asBoolean(false);
            final boolean queryRuntime = !recursive && operation.get(INCLUDE_RUNTIME).asBoolean(false);
            final boolean proxies = operation.get(PROXIES).asBoolean(false);
            final boolean defaults = operation.get(INCLUDE_DEFAULTS).asBoolean(true);

            // Attributes read directly from the model with no special read handler step in the middle
            final Map<String, ModelNode> directAttributes = new HashMap<String, ModelNode>();
            // Children names read directly from the model with no special read handler step in the middle
            final Map<String, ModelNode> directChildren = new HashMap<String, ModelNode>();
            // Attributes of AccessType.METRIC
            final Map<String, ModelNode> metrics = queryRuntime ? new HashMap<String, ModelNode>() : Collections.<String, ModelNode>emptyMap();
            // Non-AccessType.METRIC attributes with a special read handler registered
            final Map<String, ModelNode> otherAttributes = new HashMap<String, ModelNode>();
            // Child resources recursively read
            final Map<PathElement, ModelNode> childResources = recursive ? new HashMap<PathElement, ModelNode>() : Collections.<PathElement, ModelNode>emptyMap();

            // We're going to add a bunch of steps that should immediately follow this one. We are going to add them
            // in reverse order of how they should execute, as that is the way adding a Stage.IMMEDIATE step works

            // Last to execute is the handler that assembles the overall response from the pieces created by all the other steps
            final ReadResourceAssemblyHandler assemblyHandler = new ReadResourceAssemblyHandler(directAttributes, metrics, otherAttributes, directChildren, childResources);
            context.addStep(assemblyHandler, queryRuntime ? OperationContext.Stage.VERIFY : OperationContext.Stage.IMMEDIATE);
            final ImmutableManagementResourceRegistration registry = context.getResourceRegistration();
            final Resource resource = context.readResource(PathAddress.EMPTY_ADDRESS);
            // Get the model for this resource.
            final ModelNode model = resource.getModel();


            final Map<String, Set<String>> childrenByType = registry != null ? getChildAddresses(registry, resource, null) : Collections.<String, Set<String>>emptyMap();

            if (model.isDefined()) {
                // Store direct attributes first
                for (String key : model.keys()) {
                    // In case someone put some garbage in it
                    if (!childrenByType.containsKey(key)) {
                        directAttributes.put(key, model.get(key));
                    }
                }
                if (defaults) {
                    //get the model description
                    final DescriptionProvider descriptionProvider = registry.getModelDescription(PathAddress.EMPTY_ADDRESS);
                    final Locale locale = getLocale(operation);
                    final ModelNode nodeDescription = descriptionProvider.getModelDescription(locale);

                    if (nodeDescription.isDefined() && nodeDescription.hasDefined(ATTRIBUTES)) {
                        for (String key : nodeDescription.get(ATTRIBUTES).keys()) {
                            if ((!childrenByType.containsKey(key)) &&
                                    (!directAttributes.containsKey(key) || !directAttributes.get(key).isDefined()) &&
                                    nodeDescription.get(ATTRIBUTES).hasDefined(key) &&
                                    nodeDescription.get(ATTRIBUTES, key).hasDefined(DEFAULT)) {
                                directAttributes.put(key, nodeDescription.get(ATTRIBUTES, key, DEFAULT));
                            }
                        }
                    }
                }
            }

            // Next, process child resources
            for (Map.Entry<String, Set<String>> entry : childrenByType.entrySet()) {
                String childType = entry.getKey();
                Set<String> children = entry.getValue();
                if (children.isEmpty()) {
                    // Just treat it like an undefined attribute
                    directAttributes.put(childType, new ModelNode());
                } else {
                    for (String child : children) {
                        boolean storeDirect = !recursive;
                        if (recursive) {
                            PathElement childPE = PathElement.pathElement(childType, child);
                            PathAddress relativeAddr = PathAddress.pathAddress(childPE);
                            ImmutableManagementResourceRegistration childReg = registry.getSubModel(relativeAddr);
                            if (childReg == null) {
                                throw new OperationFailedException(new ModelNode().set(String.format("no child registry for (%s, %s)", childType, child)));
                            }
                            // We only invoke runtime resources if they are remote proxies
                            if (childReg.isRuntimeOnly() && (!proxies || !childReg.isRemote())) {
                                storeDirect = true;
                            } else {
                                // Add a step to read the child resource
                                ModelNode rrOp = new ModelNode();
                                rrOp.get(OP).set(opName);
                                rrOp.get(OP_ADDR).set(PathAddress.pathAddress(address, childPE).toModelNode());
                                rrOp.get(RECURSIVE).set(true);
                                rrOp.get(PROXIES).set(proxies);
                                rrOp.get(INCLUDE_RUNTIME).set(queryRuntime);
                                ModelNode rrRsp = new ModelNode();
                                childResources.put(childPE, rrRsp);

                                OperationStepHandler rrHandler = childReg.getOperationHandler(relativeAddr, opName);
                                context.addStep(rrRsp, rrOp, rrHandler, OperationContext.Stage.IMMEDIATE);
                            }
                        }
                        if (storeDirect) {
                            ModelNode childMap = directChildren.get(childType);
                            if (childMap == null) {
                                childMap = new ModelNode();
                                childMap.setEmptyObject();
                                directChildren.put(childType, childMap);
                            }
                            // Add a "child" => undefined
                            childMap.get(child);
                        }
                    }
                }
            }

            // Last, handle attributes with read handlers registered
            final Set<String> attributeNames = registry != null ? registry.getAttributeNames(PathAddress.EMPTY_ADDRESS) : Collections.<String>emptySet();
            for (final String attributeName : attributeNames) {
                final AttributeAccess access = registry.getAttributeAccess(PathAddress.EMPTY_ADDRESS, attributeName);
                if (access == null) {
                    continue;
                } else {
                    final AttributeAccess.Storage storage = access.getStorageType();
                    if (!queryRuntime && storage != AttributeAccess.Storage.CONFIGURATION) {
                        continue;
                    }
                    final AccessType type = access.getAccessType();
                    final OperationStepHandler handler = access.getReadHandler();
                    if (handler != null) {
                        // Discard any directAttribute map entry for this, as the read handler takes precedence
                        directAttributes.remove(attributeName);
                        // Create the attribute operation
                        final ModelNode attributeOperation = new ModelNode();
                        attributeOperation.get(OP_ADDR).set(opAddr);
                        attributeOperation.get(OP).set(READ_ATTRIBUTE_OPERATION);
                        attributeOperation.get(NAME).set(attributeName);

                        final ModelNode attrResponse = new ModelNode();
                        if (type == AccessType.METRIC) {
                            metrics.put(attributeName, attrResponse);
                        } else {
                            otherAttributes.put(attributeName, attrResponse);
                        }
                        context.addStep(attrResponse, attributeOperation, handler, OperationContext.Stage.IMMEDIATE);
                    }
                }
            }
            context.completeStep();
        }
    }

    ;

    /**
     * Assembles the response to a read-resource request from the components gathered by earlier steps.
     */
    private static class ReadResourceAssemblyHandler implements OperationStepHandler {

        private final Map<String, ModelNode> directAttributes;
        private final Map<String, ModelNode> directChildren;
        private final Map<String, ModelNode> metrics;
        private final Map<String, ModelNode> otherAttributes;
        private final Map<PathElement, ModelNode> childResources;

        /**
         * Creates a ReadResourceAssemblyHandler that will assemble the response using the contents
         * of the given maps.
         *
         * @param directAttributes
         * @param metrics          map of attributes of AccessType.METRIC. Keys are the attribute names, values are the full
         *                         read-attribute response from invoking the attribute's read handler. Will not be {@code null}
         * @param otherAttributes  map of attributes not of AccessType.METRIC that have a read handler registered. Keys
         *                         are the attribute names, values are the full read-attribute response from invoking the
         *                         attribute's read handler. Will not be {@code null}
         * @param directChildren
         * @param childResources   read-resource response from child resources, where the key is the PathAddress
         *                         relative to the address of the operation this handler is handling and the
         *                         value is the full read-resource response. Will not be {@code null}
         */
        private ReadResourceAssemblyHandler(final Map<String, ModelNode> directAttributes, final Map<String, ModelNode> metrics,
                                            final Map<String, ModelNode> otherAttributes, Map<String, ModelNode> directChildren, final Map<PathElement, ModelNode> childResources) {
            this.directAttributes = directAttributes;
            this.metrics = metrics;
            this.otherAttributes = otherAttributes;
            this.directChildren = directChildren;
            this.childResources = childResources;
        }

        @Override
        public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {

            Map<String, ModelNode> sortedAttributes = new TreeMap<String, ModelNode>();
            Map<String, ModelNode> sortedChildren = new TreeMap<String, ModelNode>();
            boolean failed = false;
            for (Map.Entry<String, ModelNode> entry : otherAttributes.entrySet()) {
                ModelNode value = entry.getValue();
                if (!value.has(FAILURE_DESCRIPTION)) {
                    sortedAttributes.put(entry.getKey(), value.get(RESULT));
                } else if (!failed && value.hasDefined(FAILURE_DESCRIPTION)) {
                    context.getFailureDescription().set(value.get(FAILURE_DESCRIPTION));
                    failed = true;
                    break;
                }
            }
            if (!failed) {
                for (Map.Entry<PathElement, ModelNode> entry : childResources.entrySet()) {
                    PathElement path = entry.getKey();
                    ModelNode value = entry.getValue();
                    if (!value.has(FAILURE_DESCRIPTION)) {
                        ModelNode childTypeNode = sortedChildren.get(path.getKey());
                        if (childTypeNode == null) {
                            childTypeNode = new ModelNode();
                            sortedChildren.put(path.getKey(), childTypeNode);
                        }
                        childTypeNode.get(path.getValue()).set(value.get(RESULT));
                    } else if (!failed && value.hasDefined(FAILURE_DESCRIPTION)) {
                        context.getFailureDescription().set(value.get(FAILURE_DESCRIPTION));
                        failed = true;
                    }
                }
            }
            if (!failed) {
                for (Map.Entry<String, ModelNode> simpleAttribute : directAttributes.entrySet()) {
                    sortedAttributes.put(simpleAttribute.getKey(), simpleAttribute.getValue());
                }
                for (Map.Entry<String, ModelNode> directChild : directChildren.entrySet()) {
                    sortedChildren.put(directChild.getKey(), directChild.getValue());
                }
                for (Map.Entry<String, ModelNode> metric : metrics.entrySet()) {
                    ModelNode value = metric.getValue();
                    if (!value.has(FAILURE_DESCRIPTION)) {
                        sortedAttributes.put(metric.getKey(), value.get(RESULT));
                    }
                    // we ignore metric failures
                    // TODO how to prevent the metric failure screwing up the overall context?
                }

                final ModelNode result = context.getResult();
                result.setEmptyObject();
                for (Map.Entry<String, ModelNode> entry : sortedAttributes.entrySet()) {
                    result.get(entry.getKey()).set(entry.getValue());
                }

                for (Map.Entry<String, ModelNode> entry : sortedChildren.entrySet()) {
                    result.get(entry.getKey()).set(entry.getValue());
                }
            }

            context.completeStep();
        }
    }

    /**
     * {@link org.jboss.as.controller.OperationStepHandler} reading a single attribute at the given operation address. The required request parameter "name" represents the attribute name.
     */
    public static class ReadAttributeHandler extends AbstractMultiTargetHandler implements OperationStepHandler {

        private ParametersValidator validator = new ParametersValidator();

        public ReadAttributeHandler() {
            validator.registerValidator(NAME, new StringLengthValidator(1));
            validator.registerValidator(INCLUDE_DEFAULTS, new ModelTypeValidator(ModelType.BOOLEAN, true));
        }

        @Override
        public void doExecute(OperationContext context, ModelNode operation) throws OperationFailedException {
            validator.validate(operation);
            final String attributeName = operation.require(NAME).asString();
            final boolean defaults = operation.get(INCLUDE_DEFAULTS).asBoolean(true);

            final ModelNode subModel = safeReadModel(context);
            final ImmutableManagementResourceRegistration registry = context.getResourceRegistration();
            final AttributeAccess attributeAccess = registry.getAttributeAccess(PathAddress.EMPTY_ADDRESS, attributeName);


            if (attributeAccess == null) {
                final Set<String> children = context.getResourceRegistration().getChildNames(PathAddress.EMPTY_ADDRESS);
                if (children.contains(attributeName)) {
                    throw new OperationFailedException(new ModelNode().set(String.format("'%s' is a registered child of resource (%s)", attributeName, operation.get(OP_ADDR)))); // TODO i18n
                } else if (subModel.hasDefined(attributeName)) {
                    final ModelNode result = subModel.get(attributeName);
                    context.getResult().set(result);
                } else {
                    // No defined value in the model. See if we should reply with a default from the metadata,
                    // reply with undefined, or fail because it's a non-existent attribute name
                    final ModelNode nodeDescription = getNodeDescription(registry, operation);
                    if (defaults && nodeDescription.get(ATTRIBUTES).hasDefined(attributeName) &&
                            nodeDescription.get(ATTRIBUTES, attributeName).hasDefined(DEFAULT)) {
                        final ModelNode result = nodeDescription.get(ATTRIBUTES, attributeName, DEFAULT);
                        context.getResult().set(result);
                    } else if (subModel.has(attributeName) || nodeDescription.get(ATTRIBUTES).has(attributeName)) {
                        // model had no defined value, but we treat its existence in the model or the metadata
                        // as proof that it's a legit attribute name
                        context.getResult(); // this initializes the "result" to ModelType.UNDEFINED
                    } else {
                        throw new OperationFailedException(new ModelNode().set(String.format("No known attribute %s", attributeName))); // TODO i18n
                    }
                }
                // Complete the step for the unregistered attribute case
                context.completeStep();
            } else if (attributeAccess.getReadHandler() == null) {
                // We know the attribute name is legit as it's in the registry, so this case is simpler
                if (subModel.hasDefined(attributeName) || !defaults) {
                    final ModelNode result = subModel.get(attributeName);
                    context.getResult().set(result);
                } else {
                    // It wasn't in the model, but user wants a default value from metadata if there is one
                    final ModelNode nodeDescription = getNodeDescription(registry, operation);
                    if (nodeDescription.get(ATTRIBUTES).hasDefined(attributeName) &&
                            nodeDescription.get(ATTRIBUTES, attributeName).hasDefined(DEFAULT)) {
                        final ModelNode result = nodeDescription.get(ATTRIBUTES, attributeName, DEFAULT);
                        context.getResult().set(result);
                    } else {
                        context.getResult(); // this initializes the "result" to ModelType.UNDEFINED
                    }
                }
                // Complete the step for the "registered attribute but default read handler" case
                context.completeStep();
            } else {
                OperationStepHandler handler = attributeAccess.getReadHandler();
                ClassLoader oldTccl = SecurityActions.setThreadContextClassLoader(handler.getClass());
                try {
                    handler.execute(context, operation);
                } finally {
                    SecurityActions.setThreadContextClassLoader(oldTccl);
                }
                // no context.completeStep() here as that's the read handler's job
            }
        }

        private ModelNode getNodeDescription(ImmutableManagementResourceRegistration registry, ModelNode operation) {
            final DescriptionProvider descriptionProvider = registry.getModelDescription(PathAddress.EMPTY_ADDRESS);
            final Locale locale = getLocale(operation);
            return descriptionProvider.getModelDescription(locale);
        }
    }

    ;

    /**
     * {@link org.jboss.as.controller.OperationStepHandler} writing a single attribute. The required request parameter "name" represents the attribute name.
     */
    public static class WriteAttributeHandler implements OperationStepHandler {

        private ParametersValidator nameValidator = new ParametersValidator();

        public WriteAttributeHandler() {
            nameValidator.registerValidator(NAME, new StringLengthValidator(1));
        }

        public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
            nameValidator.validate(operation);
            final String attributeName = operation.require(NAME).asString();
            final AttributeAccess attributeAccess = context.getResourceRegistration().getAttributeAccess(PathAddress.EMPTY_ADDRESS, attributeName);
            if (attributeAccess == null) {
                throw new OperationFailedException(new ModelNode().set("No known attribute called " + attributeName)); // TODO i18n
            } else if (attributeAccess.getAccessType() != AccessType.READ_WRITE) {
                throw new OperationFailedException(new ModelNode().set("Attribute " + attributeName + " is not writeable")); // TODO i18n
            } else {
                OperationStepHandler handler = attributeAccess.getWriteHandler();
                ClassLoader oldTccl = SecurityActions.setThreadContextClassLoader(handler.getClass());
                try {
                    handler.execute(context, operation);
                } finally {
                    SecurityActions.setThreadContextClassLoader(oldTccl);
                }
            }
        }
    }

    ;

    /**
     * {@link org.jboss.as.controller.OperationStepHandler} querying the children names of a given "child-type".
     */
    public static class ReadChildrenNamesOperationHandler implements OperationStepHandler {

        private final ParametersValidator validator = new ParametersValidator();

        public ReadChildrenNamesOperationHandler() {
            validator.registerValidator(CHILD_TYPE, new StringLengthValidator(1));
        }

        @Override
        public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {

            validator.validate(operation);
            final String childType = operation.require(CHILD_TYPE).asString();
            final Resource resource = context.readResource(PathAddress.EMPTY_ADDRESS);
            ImmutableManagementResourceRegistration registry = context.getResourceRegistration();
            Map<String, Set<String>> childAddresses = getChildAddresses(registry, resource, childType);
            Set<String> childNames = childAddresses.get(childType);
            if (childNames == null) {
                throw new OperationFailedException(new ModelNode().set(String.format("No known child type named %s", childType))); //TODO i18n
            }
            // Sort the result
            childNames = new TreeSet<String>(childNames);
            ModelNode result = context.getResult();
            result.setEmptyList();
            for (String childName : childNames) {
                result.add(childName);
            }

            context.completeStep();
        }
    }

    /**
     * {@link org.jboss.as.controller.OperationStepHandler} querying the children resources of a given "child-type".
     */
    public static class ReadChildrenResourcesOperationHandler implements OperationStepHandler {

        private final ParametersValidator validator = new ParametersValidator();

        public ReadChildrenResourcesOperationHandler() {
            validator.registerValidator(CHILD_TYPE, new StringLengthValidator(1));
            validator.registerValidator(RECURSIVE, new ModelTypeValidator(ModelType.BOOLEAN, true));
            validator.registerValidator(INCLUDE_RUNTIME, new ModelTypeValidator(ModelType.BOOLEAN, true));
            validator.registerValidator(PROXIES, new ModelTypeValidator(ModelType.BOOLEAN, true));
            validator.registerValidator(INCLUDE_DEFAULTS, new ModelTypeValidator(ModelType.BOOLEAN, true));
        }

        @Override
        public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {

            validator.validate(operation);
            final String childType = operation.require(CHILD_TYPE).asString();

            final Set<String> childNames = context.getResourceRegistration().getChildNames(PathAddress.EMPTY_ADDRESS);
            if (!childNames.contains(childType)) {
                throw new OperationFailedException(new ModelNode().set(String.format("No known child type named %s", childType))); //TODO i18n
            }
            final Map<PathElement, ModelNode> resources = new HashMap<PathElement, ModelNode>();

            final Resource resource = context.readResource(PathAddress.EMPTY_ADDRESS);
            if (!resource.hasChildren(childType)) {
                context.getResult().setEmptyObject();
            } else {
{'fraction_non_alphanumeric': 0.0797737225139865, 'fraction_numerical': 0.001028534027334071, 'mean_word_length': 2.294655234379011, 'pattern_counts': {'":': 0, '<': 111, '<?xml version=': 0, '>': 113, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 24, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1896089', 'n_tokens_mistral': 14691, 'n_tokens_neox': 14061, 'n_words': 3774}