/* * 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.registry; import java.util.HashSet; import java.util.NoSuchElementException; import java.util.Set; import org.jboss.as.controller.OperationClientException; import org.jboss.as.controller.PathAddress; import org.jboss.as.controller.PathElement; import org.jboss.as.controller.logging.ControllerLogger; import org.jboss.dmr.ModelNode; /** * An addressable resource in the management model, representing a local model and child resources. * <p>Instances of this class are <b>not</b> thread-safe and need to be synchronized externally. * * @author Emanuel Muckenhuber */ public interface Resource extends Cloneable { /** * Get the local model. * * @return the model */ ModelNode getModel(); /** * Write the model. * * @param newModel the new model */ void writeModel(ModelNode newModel); /** * Determine whether the model of this resource is defined. * * @return {@code true} if the local model is defined */ boolean isModelDefined(); /** * Determine whether this resource has a child with the given address. In case the {@code PathElement} has * a wildcard as value, it will determine whether this resource has any resources of a given type. * * @param element the path element * @return {@code true} if there is child with the given address */ boolean hasChild(PathElement element); /** * Get a single child of this resource with the given address. If no such child exists this will return {@code null}. * * @param element the path element * @return the resource, {@code null} if there is no such child resource */ Resource getChild(PathElement element); /** * Get a single child of this resource with the given address. If no such child exists a, an exception is thrown. * * @param element the path element * @return the resource * @throws NoSuchResourceException if the child does not exist */ Resource requireChild(PathElement element); /** * Determine whether this resource has any child of a given type. * * @param childType the child type * @return {@code true} if there is any child of the given type */ boolean hasChildren(String childType); /** * Navigate the resource tree. * * @param address the address * @return the resource * @throws NoSuchResourceException if any resource in the path does not exist */ Resource navigate(PathAddress address); /** * Get a list of registered child types for this resource. * * @return the registered child types */ Set<String> getChildTypes(); /** * Get the children names for a given type. * * @param childType the child type * @return the names of registered child resources */ Set<String> getChildrenNames(String childType); /** * Get the children for a given type. * * @param childType the child type * @return the registered children */ Set<ResourceEntry> getChildren(String childType); /** * Register a child resource. * * @param address the address * @param resource the resource * @throws IllegalStateException for a duplicate entry */ void registerChild(PathElement address, Resource resource); /** * Register a child resource * * @param address the address * @param index the index at which to add the resource. Existing children with this index and higher will be shifted one uo * @param resource the resource * @throws IllegalStateException for a duplicate entry or if the resource does not support ordered children */ void registerChild(PathElement address, int index, Resource resource); /** * Remove a child resource. * * @param address the address * @return the resource */ Resource removeChild(PathElement address); /** * Return the child types for which the order matters. * * @return {@code true} if the order of the children matters. If there are no ordered * children and empty set is returned. This method should never return {@code null} */ Set<String> getOrderedChildTypes(); /** * Gets whether this resource only exists in the runtime and has no representation in the * persistent configuration model. * * @return {@code true} if the resource has no representation in the * persistent configuration model; {@code false} otherwise */ boolean isRuntime(); /** * Gets whether operations against this resource will be proxied to a remote process. * * @return {@code true} if this resource represents a remote resource; {@code false} otherwise */ boolean isProxy(); /** * Creates and returns a copy of this resource. * * @return the clone. Will not return {@code null} */ Resource clone(); /** * Creates a shallow copy of this resource, which will only have placeholder resources * for immediate children. Those placeholder resources will return an empty * {@link org.jboss.as.controller.registry.Resource#getModel() model} and will not themselves have any children. * Their presence, however, allows the caller to see what immediate children exist under the target resource. * @return the shallow copy. Will not return {@code null} */ default Resource shallowCopy() { final Resource copy = Resource.Factory.create(); copy.writeModel(getModel()); for(final String childType : getChildTypes()) { for(final Resource.ResourceEntry child : getChildren(childType)) { copy.registerChild(child.getPathElement(), PlaceholderResource.INSTANCE); } } return copy; } interface ResourceEntry extends Resource { /** * Get the name this resource was registered under. * * @return the resource name */ String getName(); /** * Get the path element this resource was registered under. * * @return the path element */ PathElement getPathElement(); } class Factory { private Factory() { } /** * Create a default resource implementation. Equivalent to {@link #create(boolean) create(false)}. * * @return the resource */ public static Resource create() { return new BasicResource(); } /** * Create a default resource implementation. * * @param runtimeOnly the value the resource should return from {@link Resource#isRuntime()} * * @return the resource */ public static Resource create(boolean runtimeOnly) { return new BasicResource(runtimeOnly); } /** * Create a default resource implementation. * * @param runtimeOnly the value the resource should return from {@link Resource#isRuntime()} * @param orderedChildTypes the names of any child types where the order of the children matters * * @return the resource */ public static Resource create(boolean runtimeOnly, Set<String> orderedChildTypes) { return new BasicResource(runtimeOnly, orderedChildTypes); } } class Tools { /** * A {@link ResourceFilter} that returns {@code false} for {@link Resource#isRuntime() runtime} and * {@link Resource#isProxy() proxy} resources. */ public static final ResourceFilter ALL_BUT_RUNTIME_AND_PROXIES_FILTER = new ResourceFilter() { @Override public boolean accepts(PathAddress address, Resource resource) { if(resource.isRuntime() || resource.isProxy()) { return false; } return true; } }; private Tools() { } /** * Recursively reads an entire resource tree, ignoring runtime-only and proxy resources, and generates * a DMR tree representing all of the non-ignored resources. * * @param resource the root resource * @return the DMR tree */ public static ModelNode readModel(final Resource resource) { return readModel(resource, -1); } /** * Reads a resource tree, recursing up to the given number of levels but ignoring runtime-only and proxy resources, * and generates a DMR tree representing all of the non-ignored resources. * * @param resource the model * @param level the number of levels to recurse, or {@code -1} for no limit * @return the DMR tree */ public static ModelNode readModel(final Resource resource, final int level) { return readModel(resource, level, ALL_BUT_RUNTIME_AND_PROXIES_FILTER); } /** * Recursively reads an entire resource tree, ignoring runtime-only and proxy resources, and generates * a DMR tree representing all of the non-ignored resources. This variant can use a resource * registration to help identify runtime-only and proxy resources more efficiently. * * @param resource the root resource * @param mrr the resource registration for {@code resource}, or {@code null} * @return the DMR tree */ public static ModelNode readModel(final Resource resource, final ImmutableManagementResourceRegistration mrr) { return readModel(resource, -1, mrr, ALL_BUT_RUNTIME_AND_PROXIES_FILTER); } /** * Reads a resource tree, recursing up to the given number of levels but ignoring runtime-only and proxy resources, * and generates a DMR tree representing all of the non-ignored resources. This variant can use a resource * registration to help identify runtime-only and proxy resources more efficiently. * * @param resource the model * @param level the number of levels to recurse, or {@code -1} for no limit * @param mrr the resource registration for {@code resource}, or {@code null} * @return the DMR tree */ public static ModelNode readModel(final Resource resource, final int level, final ImmutableManagementResourceRegistration mrr) { return readModel(resource, level, mrr, ALL_BUT_RUNTIME_AND_PROXIES_FILTER); } /** * Reads a resource tree, recursing up to the given number of levels but ignoring resources not accepted * by the given {@code filter}, and generates a DMR tree representing all of the non-ignored resources. * * @param resource the model * @param level the number of levels to recurse, or {@code -1} for no limit * @param filter a resource filter * @return the model */ public static ModelNode readModel(final Resource resource, final int level, final ResourceFilter filter) { return readModel(resource, level, null, filter); } private static ModelNode readModel(final Resource resource, final int level, final ImmutableManagementResourceRegistration mrr, final ResourceFilter filter) { if (filter.accepts(PathAddress.EMPTY_ADDRESS, resource)) { return readModel(PathAddress.EMPTY_ADDRESS, resource, level, mrr, filter); } else { return new ModelNode(); } } private static ModelNode readModel(final PathAddress address, final Resource resource, final int level, final ImmutableManagementResourceRegistration mrr, final ResourceFilter filter) { final ModelNode model = resource.getModel().clone(); final boolean recursive = level == -1 || level > 0; if (recursive) { final int newLevel = level == -1 ? -1 : level - 1; Set<String> validChildTypes = mrr == null ? null : getNonIgnoredChildTypes(mrr); for (final String childType : resource.getChildTypes()) { if (validChildTypes != null && !validChildTypes.contains(childType)) { continue; } model.get(childType).setEmptyObject(); for (final ResourceEntry entry : resource.getChildren(childType)) { if (filter.accepts(address.append(entry.getPathElement()), resource)) { ImmutableManagementResourceRegistration childMrr = mrr == null ? null : mrr.getSubModel(address.append(entry.getPathElement())); model.get(childType, entry.getName()).set(readModel(entry, newLevel, childMrr, filter)); } } } } return model; } private static Set<String> getNonIgnoredChildTypes(ImmutableManagementResourceRegistration mrr) { Set<String> result = new HashSet<>(); for (PathElement pe : mrr.getChildAddresses(PathAddress.EMPTY_ADDRESS)) { ImmutableManagementResourceRegistration childMrr = mrr.getSubModel(PathAddress.pathAddress(pe)); if (childMrr != null && !childMrr.isRemote() && !childMrr.isRuntimeOnly()) { result.add(pe.getKey()); } } return result; } /** * Navigate from a parent {@code resource} to the descendant resource at the given relative {@code address}. * <p> * {@link Resource#navigate(PathAddress)} implementations can use this as a standard implementation. * </p> * * @param resource the resource the resource. Cannot be {@code null} * @param address the address the address relative to {@code resource}'s address. Cannot be {@code null} * @return the resource the descendant resource. Will not be {@code null} * @throws NoSuchResourceException if there is no descendant resource at {@code address} */ public static Resource navigate(final Resource resource, final PathAddress address) { Resource r = resource; for(final PathElement element : address) { r = r.requireChild(element); } return r; } } /** * A {@link NoSuchElementException} variant that can be thrown by {@link Resource#requireChild(PathElement)} and * {@link Resource#navigate(PathAddress)} implementations to indicate a client error when invoking a * management operation. */ class NoSuchResourceException extends NoSuchElementException implements OperationClientException { private static final long serialVersionUID = -2409240663987141424L; public NoSuchResourceException(PathElement childPath) { this(ControllerLogger.ROOT_LOGGER.childResourceNotFound(childPath)); } public NoSuchResourceException(String message) { super(message); } @Override public ModelNode getFailureDescription() { return new ModelNode(getLocalizedMessage()); } @Override public String toString() { return super.toString() + " [ " + getFailureDescription() + " ]"; } } }