/*
* JBoss, Home of Professional Open Source.
* Copyright 2014, 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.domain.controller.operations;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DOMAIN_ORGANIZATION;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.FAILURE_DESCRIPTION;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.NAME;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RESULT;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.VALUE;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
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.SimpleOperationDefinition;
import org.jboss.as.controller.SimpleOperationDefinitionBuilder;
import org.jboss.as.controller.access.management.SensitiveTargetAccessConstraintDefinition;
import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
import org.jboss.as.controller.descriptions.common.ControllerResolver;
import org.jboss.as.controller.operations.common.OrderedChildTypesAttachment;
import org.jboss.as.controller.registry.AttributeAccess;
import org.jboss.as.controller.registry.ImmutableManagementResourceRegistration;
import org.jboss.as.controller.registry.Resource;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.ModelType;
/**
* A generic model "describe" handler, returning a list of operations which is needed to create an equivalent model.
*
* @author Emanuel Muckenhuber
*/
public class GenericModelDescribeOperationHandler implements OperationStepHandler {
public static final GenericModelDescribeOperationHandler INSTANCE = new GenericModelDescribeOperationHandler("describe-model", false);
public static final SimpleOperationDefinition DEFINITION = new SimpleOperationDefinitionBuilder("describe-model", ControllerResolver.getResolver(SUBSYSTEM))
.addAccessConstraint(SensitiveTargetAccessConstraintDefinition.READ_WHOLE_CONFIG)
.setReplyType(ModelType.LIST)
.setReplyValueType(ModelType.OBJECT)
.setPrivateEntry()
.build();
private static final Set<String> ROOT_ATTRIBUTES = new HashSet<>(Arrays.asList(DOMAIN_ORGANIZATION));
private final String operationName;
private final boolean skipLocalAdd;
protected GenericModelDescribeOperationHandler(final String operationName, final boolean skipAdd) {
this.operationName = operationName;
this.skipLocalAdd = skipAdd;
}
@Override
public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
final PathAddress address = PathAddress.pathAddress(operation.require(ModelDescriptionConstants.OP_ADDR));
final PathAddressFilter filter = context.getAttachment(PathAddressFilter.KEY);
if (filter != null && ! filter.accepts(address)) {
return;
}
final ImmutableManagementResourceRegistration registration = context.getResourceRegistration();
if (registration.isAlias() || registration.isRemote() || registration.isRuntimeOnly()) {
return;
}
final Resource resource = context.readResource(PathAddress.EMPTY_ADDRESS, false);
final ModelNode result = context.getResult();
result.setEmptyList();
final ModelNode results = new ModelNode().setEmptyList();
final AtomicReference<ModelNode> failureRef = new AtomicReference<ModelNode>();
final Map<String, ModelNode> includeResults = new HashMap<String, ModelNode>();
// Step to handle failed operations
context.addStep(new OperationStepHandler() {
@Override
public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
boolean failed = false;
if (failureRef.get() != null) {
// One of our subsystems failed
context.getFailureDescription().set(failureRef.get());
failed = true;
} else {
for (final ModelNode includeRsp : includeResults.values()) {
if (includeRsp.hasDefined(FAILURE_DESCRIPTION)) {
context.getFailureDescription().set(includeRsp.get(FAILURE_DESCRIPTION));
failed = true;
break;
}
final ModelNode includeResult = includeRsp.get(RESULT);
if (includeResult.isDefined()) {
for (ModelNode op : includeResult.asList()) {
addOrderedChildTypeInfo(context, resource, op);
result.add(op);
}
}
}
}
if (!failed) {
for (final ModelNode childRsp : results.asList()) {
addOrderedChildTypeInfo(context, resource, childRsp);
result.add(childRsp);
}
context.getResult().set(result);
}
}
}, OperationContext.Stage.MODEL, true);
final Set<String> children = resource.getChildTypes();
for (final String childType : children) {
for (final Resource.ResourceEntry entry : resource.getChildren(childType)) {
final PathElement childPE = entry.getPathElement();
final PathAddress relativeChildAddress = PathAddress.EMPTY_ADDRESS.append(childPE);
final ImmutableManagementResourceRegistration childRegistration = registration.getSubModel(relativeChildAddress);
if (childRegistration.isRuntimeOnly()
|| childRegistration.isRemote()
|| childRegistration.isAlias()) {
continue;
}
final PathAddress absoluteChildAddr = address.append(childPE);
// Skip ignored addresses
if (filter != null && !filter.accepts(absoluteChildAddr)) {
continue;
}
final OperationStepHandler stepHandler = childRegistration.getOperationHandler(PathAddress.EMPTY_ADDRESS, operationName);
final ModelNode childRsp = new ModelNode();
context.addStep(new OperationStepHandler() {
@Override
public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
if (failureRef.get() == null) {
if (childRsp.hasDefined(FAILURE_DESCRIPTION)) {
failureRef.set(childRsp.get(FAILURE_DESCRIPTION));
} else if (childRsp.hasDefined(RESULT)) {
addChildOperation(address, childRsp.require(RESULT).asList(), results);
}
}
}
}, OperationContext.Stage.MODEL, true);
final ModelNode childOperation = operation.clone();
childOperation.get(ModelDescriptionConstants.OP).set(operationName);
childOperation.get(ModelDescriptionConstants.OP_ADDR).set(absoluteChildAddr.toModelNode());
context.addStep(childRsp, childOperation, stepHandler, OperationContext.Stage.MODEL, true);
}
}
if (resource.isProxy() || resource.isRuntime()) {
return;
}
// Generic operation generation
final ModelNode model = resource.getModel();
final OperationStepHandler addHandler = registration.getOperationHandler(PathAddress.EMPTY_ADDRESS, ModelDescriptionConstants.ADD);
if (addHandler != null) {
final ModelNode add = new ModelNode();
add.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.ADD);
add.get(ModelDescriptionConstants.OP_ADDR).set(address.toModelNode());
final Set<String> attributes = registration.getAttributeNames(PathAddress.EMPTY_ADDRESS);
for (final String attribute : attributes) {
if (!model.hasDefined(attribute)) {
continue;
}
// Process attributes
final AttributeAccess attributeAccess = registration.getAttributeAccess(PathAddress.EMPTY_ADDRESS, attribute);
if (attributeAccess.getStorageType() == AttributeAccess.Storage.CONFIGURATION) {
add.get(attribute).set(model.get(attribute));
}
}
// Allow the profile describe handler to process profile includes
processMore(context, operation, resource, address, includeResults);
if (!skipLocalAdd) {
addOrderedChildTypeInfo(context, resource, add);
result.add(add);
}
} else {
// Create write attribute operations
final Set<String> attributes = registration.getAttributeNames(PathAddress.EMPTY_ADDRESS);
for (final String attribute : attributes) {
if (!model.hasDefined(attribute)) {
continue;
}
if (address.size() == 0 && !ROOT_ATTRIBUTES.contains(attribute)) {
continue;
}
// Process attributes
final AttributeAccess attributeAccess = registration.getAttributeAccess(PathAddress.EMPTY_ADDRESS, attribute);
if (attributeAccess.getStorageType() == AttributeAccess.Storage.CONFIGURATION) {
final ModelNode writeAttribute = new ModelNode();
writeAttribute.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.WRITE_ATTRIBUTE_OPERATION);
writeAttribute.get(ModelDescriptionConstants.OP_ADDR).set(address.toModelNode());
writeAttribute.get(NAME).set(attribute);
writeAttribute.get(VALUE).set(model.get(attribute));
addOrderedChildTypeInfo(context, resource, writeAttribute);
result.add(writeAttribute);
}
}
}
}
private void addOrderedChildTypeInfo(OperationContext context, Resource resource, ModelNode operation) {
OrderedChildTypesAttachment attachment = context.getAttachment(OrderedChildTypesAttachment.KEY);
if (attachment != null) {
attachment.addOrderedChildResourceTypes(PathAddress.pathAddress(operation.get(OP_ADDR)), resource);
}
}
protected void addChildOperation(final PathAddress parent, final List<ModelNode> operations, ModelNode results) {
for (final ModelNode operation : operations) {
results.add(operation);
}
}
protected void processMore(final OperationContext context, final ModelNode operation, final Resource resource, final PathAddress address, final Map<String, ModelNode> includeResults) throws OperationFailedException {
}
}