/* * JBoss, Home of Professional Open Source. * Copyright 2011, 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.common; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DESCRIBE; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM; import java.util.Collections; import java.util.Comparator; import java.util.EnumSet; import java.util.Locale; import java.util.Set; 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.SimpleOperationDefinition; import org.jboss.as.controller.SimpleOperationDefinitionBuilder; import org.jboss.as.controller.access.Action; import org.jboss.as.controller.access.AuthorizationResult; import org.jboss.as.controller.access.management.SensitiveTargetAccessConstraintDefinition; import org.jboss.as.controller.descriptions.DescriptionProvider; import org.jboss.as.controller.descriptions.ModelDescriptionConstants; import org.jboss.as.controller.descriptions.common.ControllerResolver; import org.jboss.as.controller.logging.ControllerLogger; 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 handler recursively creating add operations for a managed resource using it's * attributes as the request-parameters. * * @author Emanuel Muckenhuber */ public class GenericSubsystemDescribeHandler implements OperationStepHandler, DescriptionProvider { public static final GenericSubsystemDescribeHandler INSTANCE = new GenericSubsystemDescribeHandler(); public static final SimpleOperationDefinition DEFINITION = new SimpleOperationDefinitionBuilder(DESCRIBE, ControllerResolver.getResolver(SUBSYSTEM)) .addAccessConstraint(SensitiveTargetAccessConstraintDefinition.READ_WHOLE_CONFIG) .setReplyType(ModelType.LIST) .setReplyValueType(ModelType.OBJECT) .setPrivateEntry() .build(); public static final Set<Action.ActionEffect> DESCRIBE_EFFECTS = Collections.unmodifiableSet(EnumSet.of(Action.ActionEffect.ADDRESS, Action.ActionEffect.READ_CONFIG)); /** Operation attachment key used when the describe operation is being invoked in order to create the operations to launch a server */ public static final OperationContext.AttachmentKey<Boolean> SERVER_LAUNCH_KEY = OperationContext.AttachmentKey.create(Boolean.class); private final Comparator<PathElement> comparator; protected GenericSubsystemDescribeHandler() { this(null); } /** * Creates a new describe handler. * <p/> * If the comparator is not {@code null} the {@link ImmutableManagementResourceRegistration#getChildAddresses(org.jboss.as.controller.PathAddress)} * child addresses} from the registration are placed in a sorted collection. This allows the result to order the * add operations for the subsystem resources. * <p/> * If the comparator is {@code null} the order for the {@link ImmutableManagementResourceRegistration#getChildAddresses(org.jboss.as.controller.PathAddress)} * child addresses} is not guaranteed. * * @param comparator the comparator used to sort the child addresses */ protected GenericSubsystemDescribeHandler(final Comparator<PathElement> comparator) { this.comparator = comparator; } /** * Creates a new describe handler. * <p/> * If the comparator is <b>not</b> {@code null} the {@link ImmutableManagementResourceRegistration#getChildAddresses(org.jboss.as.controller.PathAddress)} * child addresses} from the registration are placed in a sorted collection. This allows the result to order the * add operations for the subsystem resources. * <p/> * If the comparator is {@code null} the order for the {@link ImmutableManagementResourceRegistration#getChildAddresses(org.jboss.as.controller.PathAddress)} * child addresses} is not guaranteed. * * @param comparator the comparator used to sort the child addresses */ public static GenericSubsystemDescribeHandler create(final Comparator<PathElement> comparator) { return new GenericSubsystemDescribeHandler(comparator); } @Override public void execute(final OperationContext context, final ModelNode operation) throws OperationFailedException { final ModelNode address; final PathAddress pa = context.getCurrentAddress(); AuthorizationResult authResult = context.authorize(operation, DESCRIBE_EFFECTS); if (authResult.getDecision() != AuthorizationResult.Decision.PERMIT) { throw ControllerLogger.ROOT_LOGGER.unauthorized(operation.require(OP).asString(), pa, authResult.getExplanation()); } if (pa.size() > 0) { address = new ModelNode().add(pa.getLastElement().getKey(), pa.getLastElement().getValue()); } else { address = new ModelNode().setEmptyList(); } final Resource resource = context.readResource(PathAddress.EMPTY_ADDRESS); final ModelNode result = context.getResult(); describe(context.getAttachment(OrderedChildTypesAttachment.KEY), resource, address, result, context.getResourceRegistration()); } protected void describe(final OrderedChildTypesAttachment orderedChildTypesAttachment, final Resource resource, final ModelNode address, ModelNode result, final ImmutableManagementResourceRegistration registration) { if(resource == null || registration.isRemote() || registration.isRuntimeOnly() || resource.isProxy() || resource.isRuntime() || registration.isAlias()) { return; } final Set<PathElement> children; if (comparator == null) { children = registration.getChildAddresses(PathAddress.EMPTY_ADDRESS); } else { children = new TreeSet<PathElement>(comparator); children.addAll(registration.getChildAddresses(PathAddress.EMPTY_ADDRESS)); } result.add(createAddOperation(orderedChildTypesAttachment, address, resource, children)); for(final PathElement element : children) { if(element.isMultiTarget()) { final String childType = element.getKey(); for(final Resource.ResourceEntry entry : resource.getChildren(childType)) { final ImmutableManagementResourceRegistration childRegistration = registration.getSubModel(PathAddress.pathAddress(PathElement.pathElement(childType, entry.getName()))); final ModelNode childAddress = address.clone(); childAddress.add(childType, entry.getName()); describe(orderedChildTypesAttachment, entry, childAddress, result, childRegistration); } } else { final Resource child = resource.getChild(element); final ImmutableManagementResourceRegistration childRegistration = registration.getSubModel(PathAddress.pathAddress(element)); final ModelNode childAddress = address.clone(); childAddress.add(element.getKey(), element.getValue()); describe(orderedChildTypesAttachment, child, childAddress, result, childRegistration); } } } protected ModelNode createAddOperation(final OrderedChildTypesAttachment orderedChildTypesAttachment, final ModelNode address, final Resource resource, final Set<PathElement> children) { ModelNode addOp = createAddOperation(address, resource.getModel(), children); if (orderedChildTypesAttachment != null) { orderedChildTypesAttachment.addOrderedChildResourceTypes(PathAddress.pathAddress(address), resource); } return addOp; } protected ModelNode createAddOperation(final ModelNode address, final ModelNode subModel, final Set<PathElement> children) { final ModelNode operation = subModel.clone(); operation.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.ADD); operation.get(ModelDescriptionConstants.OP_ADDR).set(address); if(children != null && ! children.isEmpty()) { for(final PathElement path : children) { if(subModel.hasDefined(path.getKey())) { subModel.remove(path.getKey()); } } } return operation; } /** * * @param locale the locale to use to generate any localized text used in the description. * May be {@code null}, in which case {@link Locale#getDefault()} should be used * * @return definition of operation * @deprecated use {@link #DEFINITION} for registration of operation */ @Override public ModelNode getModelDescription(Locale locale) { // This is a private operation, so we should not be getting requests for descriptions return DEFINITION.getDescriptionProvider().getModelDescription(locale); } }