/*
* 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.ControllerMessages.MESSAGES;
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.RECURSIVE_DEPTH;
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 static org.jboss.as.controller.descriptions.ModelDescriptionConstants.VALUE;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
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.ControllerLogger;
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.ProcessType;
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.OperationEntry.Flag;
import org.jboss.as.controller.registry.PlaceholderResource;
import org.jboss.as.controller.registry.Resource;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.ModelType;
/**
* Global {@code OperationHandler}s.
*
* @author <a href="kabir.khan@jboss.com">Kabir Khan</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 UNDEFINE_ATTRIBUTE = new UndefineAttributeHandler();
public static final OperationStepHandler WRITE_ATTRIBUTE = new WriteAttributeHandler();
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(RECURSIVE_DEPTH, new ModelTypeValidator(ModelType.INT, 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 int recursiveDepth = operation.get(RECURSIVE_DEPTH).asInt(0);
final boolean recursive = recursiveDepth > 0 ? true : operation.get(RECURSIVE).asBoolean(false);
final boolean queryRuntime = 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 LinkedHashMap<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, queryRuntime);
final ImmutableManagementResourceRegistration registry = context.getResourceRegistration();
// Get the model for this resource.
final Resource resource = nullSafeReadResource(context, registry);
final Map<String, Set<String>> childrenByType = registry != null ? getChildAddresses(registry, resource, null) : Collections.<String, Set<String>>emptyMap();
final ModelNode model = resource.getModel();
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(context, 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(MESSAGES.noChildRegistry(childType, child)));
}
// Decide if we want to invoke on this child resource
boolean proxy = childReg.isRemote();
boolean runtimeResource = childReg.isRuntimeOnly();
if (!runtimeResource || (queryRuntime && !proxy) || (proxies && proxy)) {
final int newDepth = recursiveDepth > 0 ? recursiveDepth - 1 : 0;
// 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(operation.get(RECURSIVE));
rrOp.get(RECURSIVE_DEPTH).set(newDepth);
rrOp.get(PROXIES).set(proxies);
rrOp.get(INCLUDE_RUNTIME).set(queryRuntime);
ModelNode rrRsp = new ModelNode();
childResources.put(childPE, rrRsp);
OperationStepHandler rrHandler = childReg.getOperationHandler(PathAddress.EMPTY_ADDRESS, opName);
context.addStep(rrRsp, rrOp, rrHandler, OperationContext.Stage.IMMEDIATE);
} else {
storeDirect = true;
}
}
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(OperationContext.RollbackHandler.NOOP_ROLLBACK_HANDLER);
}
/**
* Provides a resource for the current step, either from the context, if the context doesn't have one
* and {@code registry} is runtime-only, it creates a dummy resource.
*/
private static Resource nullSafeReadResource(final OperationContext context, final ImmutableManagementResourceRegistration registry) {
Resource result;
if (registry != null && registry.isRuntimeOnly()) {
try {
result = context.readResource(PathAddress.EMPTY_ADDRESS, false);
} catch (RuntimeException e) {
result = PlaceholderResource.INSTANCE;
}
} else {
result = context.readResource(PathAddress.EMPTY_ADDRESS, false);
}
return result;
}
}
/**
* 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(OperationContext.RollbackHandler.NOOP_ROLLBACK_HANDLER);
}
}
/**
* {@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(MESSAGES.attributeRegisteredOnResource(attributeName, operation.get(OP_ADDR))));
} 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, context, 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(MESSAGES.unknownAttribute(attributeName)));
}
}
// Complete the step for the unregistered attribute case
context.completeStep(OperationContext.RollbackHandler.NOOP_ROLLBACK_HANDLER);
} 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, context, 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(OperationContext.RollbackHandler.NOOP_ROLLBACK_HANDLER);
} 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, OperationContext context, ModelNode operation) throws OperationFailedException {
final DescriptionProvider descriptionProvider = registry.getModelDescription(PathAddress.EMPTY_ADDRESS);
final Locale locale = getLocale(context, 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(MESSAGES.unknownAttribute(attributeName)));
} else if (attributeAccess.getAccessType() != AccessType.READ_WRITE) {
throw new OperationFailedException(new ModelNode().set(MESSAGES.attributeNotWritable(attributeName)));
} else {
OperationStepHandler handler = attributeAccess.getWriteHandler();
ClassLoader oldTccl = SecurityActions.setThreadContextClassLoader(handler.getClass());
try {
handler.execute(context, operation);
} finally {
SecurityActions.setThreadContextClassLoader(oldTccl);
}
}
}
}
;
/**
* The undefine-attribute handler, writing an undefined value for a single attribute.
*/
public static class UndefineAttributeHandler extends WriteAttributeHandler {
@Override
public void execute(final OperationContext context, final ModelNode original) throws OperationFailedException {
final ModelNode operation = original.clone();
operation.get(VALUE).set(new ModelNode());
super.execute(context, operation);
}
}
/**
* {@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, false);
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(MESSAGES.unknownChildType(childType)));
}
// Sort the result
childNames = new TreeSet<String>(childNames);
ModelNode result = context.getResult();
result.setEmptyList();
for (String childName : childNames) {
result.add(childName);
}
context.completeStep(OperationContext.RollbackHandler.NOOP_ROLLBACK_HANDLER);
}
}
/**
* {@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(RECURSIVE_DEPTH, new ModelTypeValidator(ModelType.INT, 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 Map<PathElement, ModelNode> resources = new HashMap<PathElement, ModelNode>();
final Resource resource = context.readResource(PathAddress.EMPTY_ADDRESS, false);
final 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(MESSAGES.unknownChildType(childType)));
}
// 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 ReadChildrenResourcesAssemblyHandler assemblyHandler = new ReadChildrenResourcesAssemblyHandler(resources);
context.addStep(assemblyHandler, OperationContext.Stage.IMMEDIATE);
final PathAddress address = PathAddress.pathAddress(operation.get(OP_ADDR));
for (final String key : childNames) {
final PathElement childPath = PathElement.pathElement(childType, key);
final PathAddress childAddress = PathAddress.EMPTY_ADDRESS.append(PathElement.pathElement(childType, key));
final ModelNode readOp = new ModelNode();
readOp.get(OP).set(READ_RESOURCE_OPERATION);
readOp.get(OP_ADDR).set(PathAddress.pathAddress(address, childPath).toModelNode());
if (operation.hasDefined(INCLUDE_RUNTIME)) {
readOp.get(INCLUDE_RUNTIME).set(operation.get(INCLUDE_RUNTIME));
}
if (operation.hasDefined(RECURSIVE)) {
readOp.get(RECURSIVE).set(operation.get(RECURSIVE));
}
if(operation.hasDefined(RECURSIVE_DEPTH)) {
readOp.get(RECURSIVE_DEPTH).set(operation.get(RECURSIVE_DEPTH));
}
if (operation.hasDefined(PROXIES)) {
readOp.get(PROXIES).set(operation.get(PROXIES));
}
if (operation.hasDefined(INCLUDE_DEFAULTS)) {
readOp.get(INCLUDE_DEFAULTS).set(operation.get(INCLUDE_DEFAULTS));
}
final OperationStepHandler handler = context.getResourceRegistration().getOperationHandler(childAddress, READ_RESOURCE_OPERATION);
if (handler == null) {
throw new OperationFailedException(new ModelNode().set(MESSAGES.noOperationHandler()));
}
final ModelNode rrRsp = new ModelNode();
resources.put(childPath, rrRsp);
context.addStep(rrRsp, readOp, handler, OperationContext.Stage.IMMEDIATE);
}
context.completeStep(OperationContext.RollbackHandler.NOOP_ROLLBACK_HANDLER);
}
}
/**
* Assembles the response to a read-resource request from the components gathered by earlier steps.
*/
public static class ReadChildrenResourcesAssemblyHandler implements OperationStepHandler {
private final Map<PathElement, ModelNode> resources;
/**
* Creates a ReadResourceAssemblyHandler that will assemble the response using the contents
* of the given maps.
*
* @param resources read-resource response from child resources, where the key is the path of the resource
* 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}
*/
public ReadChildrenResourcesAssemblyHandler(final Map<PathElement, ModelNode> resources) {
this.resources = resources;
}
@Override
public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
context.addStep(new OperationStepHandler() {
@Override
public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
Map<String, ModelNode> sortedChildren = new TreeMap<String, ModelNode>();
boolean failed = false;
for (Map.Entry<PathElement, ModelNode> entry : resources.entrySet()) {
PathElement path = entry.getKey();
ModelNode value = entry.getValue();
if (!value.has(FAILURE_DESCRIPTION)) {
sortedChildren.put(path.getValue(), value.get(RESULT));
} else if (!failed && value.hasDefined(FAILURE_DESCRIPTION)) {
context.getFailureDescription().set(value.get(FAILURE_DESCRIPTION));
failed = true;
}
}
if (!failed) {
final ModelNode result = context.getResult();
result.setEmptyObject();
for (Map.Entry<String, ModelNode> entry : sortedChildren.entrySet()) {
result.get(entry.getKey()).set(entry.getValue());
}
}
context.completeStep(OperationContext.RollbackHandler.NOOP_ROLLBACK_HANDLER);
}
}, OperationContext.Stage.VERIFY);
context.completeStep(OperationContext.RollbackHandler.NOOP_ROLLBACK_HANDLER);
}
}
/**
* {@link org.jboss.as.controller.OperationStepHandler} querying the child types of a given node.
*/
public static final OperationStepHandler READ_CHILDREN_TYPES = new OperationStepHandler() {
@Override
public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
final ImmutableManagementResourceRegistration registry = context.getResourceRegistration();
Set<String> childTypes = new TreeSet<String>(registry.getChildNames(PathAddress.EMPTY_ADDRESS));
final ModelNode result = context.getResult();
result.setEmptyList();
for (final String key : childTypes) {
result.add(key);
}
context.completeStep(OperationContext.RollbackHandler.NOOP_ROLLBACK_HANDLER);
}
};
/**
* {@link org.jboss.as.controller.OperationStepHandler} returning the names of the defined operations at a given model address.
*/
public static final OperationStepHandler READ_OPERATION_NAMES = new OperationStepHandler() {
@Override
public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
final ImmutableManagementResourceRegistration registry = context.getResourceRegistration();
final Map<String, OperationEntry> operations = registry.getOperationDescriptions(PathAddress.EMPTY_ADDRESS, true);
final ModelNode result = new ModelNode();
if (operations.size() > 0) {
for (final Entry<String, OperationEntry> entry : operations.entrySet()) {
if (entry.getValue().getType() == OperationEntry.EntryType.PUBLIC) {
if ( context.getProcessType() == ProcessType.DOMAIN_SERVER ? entry.getValue().getFlags().contains(Flag.RUNTIME_ONLY) : true ) {
result.add(entry.getKey());
}
}
}
} else {
result.setEmptyList();
}
context.getResult().set(result);
context.completeStep(OperationContext.RollbackHandler.NOOP_ROLLBACK_HANDLER);
}
};
/**
* {@link org.jboss.as.controller.OperationStepHandler} returning the type description of a single operation description.
*/
public static final OperationStepHandler READ_OPERATION_DESCRIPTION = new OperationStepHandler() {
@Override
public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
String operationName = operation.require(NAME).asString();
final ImmutableManagementResourceRegistration registry = context.getResourceRegistration();
OperationEntry operationEntry = registry.getOperationEntry(PathAddress.EMPTY_ADDRESS, operationName);
if (operationEntry == null || (context.getProcessType() == ProcessType.DOMAIN_SERVER && !operationEntry.getFlags().contains(Flag.RUNTIME_ONLY))) {
throw new OperationFailedException(new ModelNode().set(MESSAGES.operationNotRegistered(operationName,
PathAddress.pathAddress(operation.require(OP_ADDR)))));
} else {
final ModelNode result = operationEntry.getDescriptionProvider().getModelDescription(getLocale(context, operation));
Set<OperationEntry.Flag> flags = operationEntry.getFlags();
boolean readOnly = flags.contains(OperationEntry.Flag.READ_ONLY);
result.get(READ_ONLY).set(readOnly);
if (!readOnly) {
if (flags.contains(OperationEntry.Flag.RESTART_ALL_SERVICES)) {
result.get(RESTART_REQUIRED).set("all-services");
} else if (flags.contains(OperationEntry.Flag.RESTART_RESOURCE_SERVICES)) {
result.get(RESTART_REQUIRED).set("resource-services");
} else if (flags.contains(OperationEntry.Flag.RESTART_JVM)) {
result.get(RESTART_REQUIRED).set("jvm");
}
}
context.getResult().set(result);
}
context.completeStep(OperationContext.RollbackHandler.NOOP_ROLLBACK_HANDLER);
}
};
/**
* {@link org.jboss.as.controller.OperationStepHandler} querying the complete type description of a given model node.
*/
public static final OperationStepHandler READ_RESOURCE_DESCRIPTION = new OperationStepHandler() {
private final ParametersValidator validator = new ParametersValidator();
{
validator.registerValidator(RECURSIVE, new ModelTypeValidator(ModelType.BOOLEAN, true));
validator.registerValidator(RECURSIVE_DEPTH, new ModelTypeValidator(ModelType.INT, true));
validator.registerValidator(PROXIES, new ModelTypeValidator(ModelType.BOOLEAN, true));
validator.registerValidator(OPERATIONS, new ModelTypeValidator(ModelType.BOOLEAN, true));
validator.registerValidator(INHERITED, new ModelTypeValidator(ModelType.BOOLEAN, true));
}
@Override
public void execute(final OperationContext context, final ModelNode operation) throws OperationFailedException {
final PathAddress address = PathAddress.pathAddress(operation.get(OP_ADDR));
if (address.isMultiTarget()) {
// Format wildcard queries as list
final ModelNode result = context.getResult().setEmptyList();
context.addStep(new ModelNode(), AbstractMultiTargetHandler.FAKE_OPERATION.clone(), new RegistrationAddressResolver(operation, result,
new OperationStepHandler() {
@Override
public void execute(final OperationContext context, final ModelNode operation) throws OperationFailedException {
// step handler bypassing further wildcard resolution
doExecute(context, operation);
}
}), OperationContext.Stage.IMMEDIATE);
context.completeStep(OperationContext.RollbackHandler.NOOP_ROLLBACK_HANDLER);
} else {
doExecute(context, operation);
}
}
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 int recursiveDepth = operation.get(RECURSIVE_DEPTH).asInt(0);
final boolean recursive = recursiveDepth > 0 ? true : operation.get(RECURSIVE).asBoolean(false);
final boolean proxies = operation.get(PROXIES).asBoolean(false);
final boolean ops = operation.get(OPERATIONS).asBoolean(false);
final boolean inheritedOps = operation.get(INHERITED).asBoolean(true);
final ImmutableManagementResourceRegistration registry = context.getResourceRegistration();
final DescriptionProvider descriptionProvider = registry.getModelDescription(PathAddress.EMPTY_ADDRESS);
final Locale locale = getLocale(context, operation);
final ModelNode nodeDescription = descriptionProvider.getModelDescription(locale);
final Map<String, ModelNode> operations = new HashMap<String, ModelNode>();
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 ReadResourceDescriptionAssemblyHandler assemblyHandler = new ReadResourceDescriptionAssemblyHandler(nodeDescription, operations, childResources);
context.addStep(assemblyHandler, OperationContext.Stage.IMMEDIATE);
if (ops) {
for (final Map.Entry<String, OperationEntry> entry : registry.getOperationDescriptions(PathAddress.EMPTY_ADDRESS, inheritedOps).entrySet()) {
if (entry.getValue().getType() == OperationEntry.EntryType.PUBLIC) {
if ( context.getProcessType() == ProcessType.DOMAIN_SERVER ? entry.getValue().getFlags().contains(Flag.RUNTIME_ONLY) : true ) {
final DescriptionProvider provider = entry.getValue().getDescriptionProvider();
operations.put(entry.getKey(), provider.getModelDescription(locale));
}
}
}
}
if (nodeDescription.hasDefined(ATTRIBUTES)) {
for (final String attr : nodeDescription.require(ATTRIBUTES).keys()) {
final AttributeAccess access = registry.getAttributeAccess(PathAddress.EMPTY_ADDRESS, attr);
// If there is metadata for an attribute but no AttributeAccess, assume RO. Can't
// be writable without a registered handler. This opens the possibility that out-of-date metadata
// for attribute "foo" can lead to a read of non-existent-in-model "foo" with
// an unexpected undefined value returned. But it removes the possibility of a
// dev forgetting to call registry.registerReadOnlyAttribute("foo", null) resulting
// in the valid attribute "foo" not being readable
final AccessType accessType = access == null ? AccessType.READ_ONLY : access.getAccessType();
final Storage storage = access == null ? Storage.CONFIGURATION : access.getStorageType();
final ModelNode attrNode = nodeDescription.get(ATTRIBUTES, attr);
//AS7-3085 - For a domain mode server show writable attributes as read-only
String displayedAccessType =
context.getProcessType() == ProcessType.DOMAIN_SERVER && storage == Storage.CONFIGURATION ?
AccessType.READ_ONLY.toString() : accessType.toString();
attrNode.get(ACCESS_TYPE).set(displayedAccessType);
attrNode.get(STORAGE).set(storage.toString());
if (accessType == AccessType.READ_WRITE) {
Set<AttributeAccess.Flag> flags = access.getFlags();
if (flags.contains(AttributeAccess.Flag.RESTART_ALL_SERVICES)) {
attrNode.get(RESTART_REQUIRED).set("all-services");
} else if (flags.contains(AttributeAccess.Flag.RESTART_RESOURCE_SERVICES)) {
attrNode.get(RESTART_REQUIRED).set("resource-services");
} else if (flags.contains(AttributeAccess.Flag.RESTART_JVM)) {
attrNode.get(RESTART_REQUIRED).set("jvm");
} else {
attrNode.get(RESTART_REQUIRED).set("no-services");
}
}
}
}
if (recursive) {
for (final PathElement element : registry.getChildAddresses(PathAddress.EMPTY_ADDRESS)) {
PathAddress relativeAddr = PathAddress.pathAddress(element);
ImmutableManagementResourceRegistration childReg = registry.getSubModel(relativeAddr);
boolean readChild = true;
if (childReg.isRemote() && !proxies) {
readChild = false;
}
if (readChild) {
final int newDepth = recursiveDepth > 0 ? recursiveDepth - 1 : 0;
ModelNode rrOp = new ModelNode();
rrOp.get(OP).set(opName);
try {
rrOp.get(OP_ADDR).set(PathAddress.pathAddress(address, element).toModelNode());
} catch (Exception e) {
continue;
}
rrOp.get(RECURSIVE).set(operation.get(RECURSIVE));
rrOp.get(RECURSIVE_DEPTH).set(newDepth);
rrOp.get(PROXIES).set(proxies);
rrOp.get(OPERATIONS).set(ops);
rrOp.get(INHERITED).set(inheritedOps);
rrOp.get(LOCALE).set(operation.get(LOCALE));
ModelNode rrRsp = new ModelNode();
childResources.put(element, rrRsp);
final OperationStepHandler handler = childReg.isRemote() ? childReg.getOperationHandler(PathAddress.EMPTY_ADDRESS, opName) :
new OperationStepHandler() {
@Override
public void execute(final OperationContext context, final ModelNode operation) throws OperationFailedException {
doExecute(context, operation);
}
};
context.addStep(rrRsp, rrOp, handler, OperationContext.Stage.IMMEDIATE);
}
//Add a "child" => undefined
nodeDescription.get(CHILDREN, element.getKey(), MODEL_DESCRIPTION, element.getValue());
}
}
context.completeStep(OperationContext.RollbackHandler.NOOP_ROLLBACK_HANDLER);
}
};
/**
* Assembles the response to a read-resource request from the components gathered by earlier steps.
*/
private static class ReadResourceDescriptionAssemblyHandler implements OperationStepHandler {
private final ModelNode nodeDescription;
private final Map<String, ModelNode> operations;
private final Map<PathElement, ModelNode> childResources;
/**
* Creates a ReadResourceAssemblyHandler that will assemble the response using the contents
* of the given maps.
*
* @param nodeDescription basic description of the node, of its attributes and of its child types
* @param operations descriptions of the resource's operations
* @param childResources read-resource-description 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 ReadResourceDescriptionAssemblyHandler(final ModelNode nodeDescription, final Map<String, ModelNode> operations, final Map<PathElement, ModelNode> childResources) {
this.nodeDescription = nodeDescription;
this.operations = operations;
this.childResources = childResources;
}
@Override
public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
for (Map.Entry<PathElement, ModelNode> entry : childResources.entrySet()) {
final PathElement element = entry.getKey();
final ModelNode value = entry.getValue();
if (!value.has(FAILURE_DESCRIPTION)) {
nodeDescription.get(CHILDREN, element.getKey(), MODEL_DESCRIPTION, element.getValue()).set(value.get(RESULT));
} else if (value.hasDefined(FAILURE_DESCRIPTION)) {
context.getFailureDescription().set(value.get(FAILURE_DESCRIPTION));
break;
}
}
for (Map.Entry<String, ModelNode> entry : operations.entrySet()) {
nodeDescription.get(OPERATIONS, entry.getKey()).set(entry.getValue());
}
context.getResult().set(nodeDescription);
context.completeStep(OperationContext.RollbackHandler.NOOP_ROLLBACK_HANDLER);
}
}
public abstract static class AbstractMultiTargetHandler implements OperationStepHandler {
public static final ModelNode FAKE_OPERATION;
static {
final ModelNode resolve = new ModelNode();
resolve.get(OP).set("resolve");
resolve.get(OP_ADDR).setEmptyList();
resolve.protect();
FAKE_OPERATION = resolve;
}
@Override
public void execute(final OperationContext context, final ModelNode operation) throws OperationFailedException {
final PathAddress address = PathAddress.pathAddress(operation.require(OP_ADDR));
// In case if it's a multiTarget operation, resolve the address first
// This only works for model resources, which can be resolved into a concrete addresses
if (address.isMultiTarget()) {
// The final result should be a list of executed operations
final ModelNode result = context.getResult().setEmptyList();
// Trick the context to give us the model-root
context.addStep(new ModelNode(), FAKE_OPERATION.clone(), new ModelAddressResolver(operation, result, new OperationStepHandler() {
@Override
public void execute(final OperationContext context, final ModelNode operation) throws OperationFailedException {
doExecute(context, operation);
}
}), OperationContext.Stage.IMMEDIATE);
context.completeStep(OperationContext.RollbackHandler.NOOP_ROLLBACK_HANDLER);
} else {
doExecute(context, operation);
}
}
/**
* Execute the actual operation if it is not addressed to multiple targets.
*
* @param context the operation context
* @param operation the original operation
* @throws OperationFailedException
*/
abstract void doExecute(OperationContext context, ModelNode operation) throws OperationFailedException;
}
public static final class ModelAddressResolver implements OperationStepHandler {
private final ModelNode operation;
private final ModelNode result;
private final OperationStepHandler handler; // handler bypassing further wildcard resolution
public ModelAddressResolver(final ModelNode operation, final ModelNode result, final OperationStepHandler delegate) {
this.operation = operation;
this.result = result;
this.handler = delegate;
}
/**
* {@inheritDoc}
*/
@Override
public void execute(final OperationContext context, final ModelNode ignored) throws OperationFailedException {
final PathAddress address = PathAddress.pathAddress(operation.require(OP_ADDR));
execute(address, PathAddress.EMPTY_ADDRESS, context);
context.completeStep(OperationContext.RollbackHandler.NOOP_ROLLBACK_HANDLER);
}
void execute(final PathAddress address, final PathAddress base, final OperationContext context) {
final Resource resource = context.readResource(base, false);
final PathAddress current = address.subAddress(base.size());
final Iterator<PathElement> iterator = current.iterator();
if (iterator.hasNext()) {
final PathElement element = iterator.next();
if (element.isMultiTarget()) {
final String childType = element.getKey().equals("*") ? null : element.getKey();
final ImmutableManagementResourceRegistration registration = context.getResourceRegistration().getSubModel(base);
if (registration.isRemote() || registration.isRuntimeOnly()) {
// At least for proxies it should use the proxy operation handler
throw new IllegalStateException();
}
final Map<String, Set<String>> resolved = getChildAddresses(registration, resource, childType);
for (Map.Entry<String, Set<String>> entry : resolved.entrySet()) {
final String key = entry.getKey();
final Set<String> children = entry.getValue();
if (children.isEmpty()) {
continue;
}
if (element.isWildcard()) {
for (final String child : children) {
// Double check if the child actually exists
if (resource.hasChild(PathElement.pathElement(key, child))) {
execute(address, base.append(PathElement.pathElement(key, child)), context);
}
}
} else {
for (final String segment : element.getSegments()) {
if (children.contains(segment)) {
// Double check if the child actually exists
if (resource.hasChild(PathElement.pathElement(key, segment))) {
execute(address, base.append(PathElement.pathElement(key, segment)), context);
}
}
}
}
}
} else {
// Double check if the child actually exists
if (resource.hasChild(element)) {
execute(address, base.append(element), context);
}
}
} else {
//final String operationName = operation.require(OP).asString();
final ModelNode newOp = operation.clone();
newOp.get(OP_ADDR).set(base.toModelNode());
final ModelNode result = this.result.add();
result.get(OP_ADDR).set(base.toModelNode());
context.addStep(result, newOp, handler, OperationContext.Stage.IMMEDIATE);
}
}
}
static class RegistrationAddressResolver implements OperationStepHandler {
private final ModelNode operation;
private final ModelNode result;
private final OperationStepHandler handler; // handler bypassing further wildcard resolution
RegistrationAddressResolver(final ModelNode operation, final ModelNode result, final OperationStepHandler delegate) {
this.operation = operation;
this.result = result;
this.handler = delegate;
}
@Override
public void execute(final OperationContext context, final ModelNode ignored) throws OperationFailedException {
final PathAddress address = PathAddress.pathAddress(operation.require(OP_ADDR));
execute(address, PathAddress.EMPTY_ADDRESS, context);
context.completeStep(OperationContext.RollbackHandler.NOOP_ROLLBACK_HANDLER);
}
void execute(final PathAddress address, PathAddress base, final OperationContext context) {
final PathAddress current = address.subAddress(base.size());
final Iterator<PathElement> iterator = current.iterator();
if (iterator.hasNext()) {
final PathElement element = iterator.next();
if (element.isMultiTarget()) {
final Set<PathElement> children = context.getResourceRegistration().getChildAddresses(base);
if (children == null || children.isEmpty()) {
return;
}
final String childType = element.getKey().equals("*") ? null : element.getKey();
for (final PathElement path : children) {
if (childType != null && !childType.equals(path.getKey())) {
continue;
}
execute(address, base.append(path), context);
}
} else {
execute(address, base.append(element), context);
}
} else {
//final String operationName = operation.require(OP).asString();
final ModelNode newOp = operation.clone();
newOp.get(OP_ADDR).set(base.toModelNode());
final ModelNode result = this.result.add();
result.get(OP_ADDR).set(base.toModelNode());
context.addStep(result, newOp, handler, OperationContext.Stage.IMMEDIATE);
}
}
}
private static ModelNode safeReadModel(final OperationContext context) {
try {
final Resource resource = context.readResource(PathAddress.EMPTY_ADDRESS, false);
final ModelNode result = resource.getModel();
if (result.isDefined()) {
return result;
}
} catch (Exception e) {
// ignore
}
return new ModelNode().setEmptyObject();
}
/**
* Gets the addresses of the child resources under the given resource.
*
* @param registry registry entry representing the resource
* @param resource the current resource
* @param validChildType a single child type to which the results should be limited. If {@code null} the result
* should include all child types
* @return map where the keys are the child types and the values are a set of child names associated with a type
*/
private static Map<String, Set<String>> getChildAddresses(final ImmutableManagementResourceRegistration registry, Resource resource, final String validChildType) {
Map<String, Set<String>> result = new HashMap<String, Set<String>>();
Set<PathElement> elements = registry.getChildAddresses(PathAddress.EMPTY_ADDRESS);
for (PathElement element : elements) {
String childType = element.getKey();
if (validChildType != null && !validChildType.equals(childType)) {
continue;
}
Set<String> set = result.get(childType);
if (set == null) {
set = new LinkedHashSet<String>();
result.put(childType, set);
if (resource.hasChildren(childType)) {
set.addAll(resource.getChildrenNames(childType));
}
}
if (!element.isWildcard()) {
ImmutableManagementResourceRegistration childReg = registry.getSubModel(PathAddress.pathAddress(element));
if (childReg != null && childReg.isRuntimeOnly()) {
set.add(element.getValue());
}
}
}
return result;
}
private static Locale getLocale(OperationContext context, final ModelNode operation) throws OperationFailedException {
if (!operation.hasDefined(LOCALE)) {
return null;
}
String unparsed = normalizeLocale(operation.get(LOCALE).asString());
int len = unparsed.length();
if (len != 2 && len != 5 && len < 7) {
reportInvalidLocaleFormat(context, unparsed);
return null;
}
char char0 = unparsed.charAt(0);
char char1 = unparsed.charAt(1);
if (char0 < 'a' || char0 > 'z' || char1 < 'a' || char1 > 'z') {
reportInvalidLocaleFormat(context, unparsed);
return null;
}
if (len == 2) {
return new Locale(unparsed, "");
}
if (!isLocaleSeparator(unparsed.charAt(2))) {
reportInvalidLocaleFormat(context, unparsed);
return null;
}
char char3 = unparsed.charAt(3);
if (isLocaleSeparator(char3)) {
// no country
return new Locale(unparsed.substring(0, 2), "", unparsed.substring(4));
}
char char4 = unparsed.charAt(4);
if (char3 < 'A' || char3 > 'Z' || char4 < 'A' || char4 > 'Z') {
reportInvalidLocaleFormat(context, unparsed);
return null;
}
if (len == 5) {
return new Locale(unparsed.substring(0, 2), unparsed.substring(3));
}
if (!isLocaleSeparator(unparsed.charAt(5))) {
reportInvalidLocaleFormat(context, unparsed);
return null;
}
return new Locale(unparsed.substring(0, 2), unparsed.substring(3, 5), unparsed.substring(6));
}
private static String normalizeLocale(String toNormalize) {
return ("zh_Hans".equalsIgnoreCase(toNormalize) || "zh-Hans".equalsIgnoreCase(toNormalize)) ? "zh_CN" : toNormalize;
}
private static boolean isLocaleSeparator(char ch) {
return ch == '-' || ch == '_';
}
private static void reportInvalidLocaleFormat(OperationContext context, String format) {
String msg = MESSAGES.invalidLocaleString(format);
ControllerLogger.MGMT_OP_LOGGER.debug(msg);
// TODO report the problem to client via out-of-band message.
// Enable this in 7.2 or later when there is time to test
//context.report(MessageSeverity.WARN, msg);
}
}