/*
* 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.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import org.jboss.as.controller.OperationDefinition;
import org.jboss.as.controller.OperationStepHandler;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.ProxyController;
import org.jboss.as.controller.ResourceDefinition;
import org.jboss.as.controller.SimpleResourceDefinition;
import org.jboss.as.controller.access.management.AccessConstraintDefinition;
import org.jboss.as.controller.capability.RuntimeCapability;
import org.jboss.as.controller.descriptions.DescriptionProvider;
import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
import org.jboss.as.controller.descriptions.OverrideDescriptionProvider;
import org.jboss.as.controller.logging.ControllerLogger;
import org.jboss.dmr.ModelNode;
import org.wildfly.common.Assert;
/**
* A registry of model node information. This registry is thread-safe.
*
* @author <a href="mailto:david.lloyd@redhat.com">David M. Lloyd</a>
*/
@SuppressWarnings("deprecation")
abstract class AbstractResourceRegistration implements ManagementResourceRegistration {
private final String valueString;
private final NodeSubregistry parent;
private final PathAddress pathAddress;
private RootInvocation rootInvocation;
AbstractResourceRegistration(final String valueString, final NodeSubregistry parent) {
checkPermission();
this.valueString = valueString;
this.parent = parent;
this.pathAddress = parent == null ? PathAddress.EMPTY_ADDRESS : parent.getPathAddress(valueString);
}
static void checkPermission() {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(ImmutableManagementResourceRegistration.ACCESS_PERMISSION);
}
}
NodeSubregistry getParentSubRegistry() {
return parent;
}
void addAccessConstraints(List<AccessConstraintDefinition> list) {
// no-op in the base class
}
/** {@inheritDoc} */
@Override
public abstract ManagementResourceRegistration registerSubModel(final ResourceDefinition resourceDefinition);
@Override
public boolean isAllowsOverride() {
checkPermission();
return !isRemote() && parent != null && PathElement.WILDCARD_VALUE.equals(valueString);
}
@Override
public ManagementResourceRegistration registerOverrideModel(String name, OverrideDescriptionProvider descriptionProvider) {
Assert.checkNotNullParam("name", name);
Assert.checkNotNullParam("descriptionProvider", descriptionProvider);
if (parent == null) {
throw ControllerLogger.ROOT_LOGGER.cannotOverrideRootRegistration();
}
if (!PathElement.WILDCARD_VALUE.equals(valueString)) {
throw ControllerLogger.ROOT_LOGGER.cannotOverrideNonWildCardRegistration(valueString);
}
PathElement pe = PathElement.pathElement(parent.getKeyName(), name);
final SimpleResourceDefinition rd = new SimpleResourceDefinition(pe, new OverrideDescriptionCombiner(getModelDescription(PathAddress.EMPTY_ADDRESS), descriptionProvider)) {
@Override
public List<AccessConstraintDefinition> getAccessConstraints() {
return AbstractResourceRegistration.this.getAccessConstraints();
}
};
return parent.getParent().registerSubModel(rd);
}
@Override
public void unregisterOverrideModel(String name) {
Assert.checkNotNullParam("name", name);
if (PathElement.WILDCARD_VALUE.equals(name)) {
throw ControllerLogger.ROOT_LOGGER.wildcardRegistrationIsNotAnOverride();
}
if (parent == null) {
throw ControllerLogger.ROOT_LOGGER.rootRegistrationIsNotOverridable();
}
PathElement pe = PathElement.pathElement(parent.getKeyName(), name);
parent.getParent().unregisterSubModel(pe);
}
@Override
public void registerOperationHandler(OperationDefinition definition, OperationStepHandler handler){
registerOperationHandler(definition, handler, false);
}
@Override
public abstract void registerOperationHandler(OperationDefinition definition, OperationStepHandler handler,boolean inherited);
/** {@inheritDoc} */
@Override
public abstract void unregisterOperationHandler(final String operationName);
/** {@inheritDoc} */
@Override
public abstract void registerProxyController(final PathElement address, final ProxyController controller) throws IllegalArgumentException;
/** {@inheritDoc} */
@Override
public abstract void unregisterProxyController(PathElement address);
/** {@inheritDoc} */
@Override
public final OperationEntry getOperationEntry(final PathAddress pathAddress, final String operationName) {
if (parent != null) {
RootInvocation ri = getRootInvocation();
return ri.root.getOperationEntry(ri.pathAddress.append(pathAddress), operationName);
}
// else we are the root
OperationEntry inheritable = getInheritableOperationEntry(operationName);
return getOperationEntry(pathAddress.iterator(), operationName, inheritable);
}
abstract OperationEntry getOperationEntry(ListIterator<PathElement> iterator, String operationName, OperationEntry inherited);
abstract OperationEntry getInheritableOperationEntry(String operationName);
/** {@inheritDoc} */
@Override
public final OperationStepHandler getOperationHandler(final PathAddress pathAddress, final String operationName) {
OperationEntry entry = getOperationEntry(pathAddress, operationName);
return entry == null ? null : entry.getOperationHandler();
}
/** {@inheritDoc} */
@Override
public final DescriptionProvider getOperationDescription(final PathAddress address, final String operationName) {
OperationEntry entry = getOperationEntry(address, operationName);
return entry == null ? null : entry.getDescriptionProvider();
}
/** {@inheritDoc} */
@Override
public final Set<OperationEntry.Flag> getOperationFlags(final PathAddress pathAddress, final String operationName) {
OperationEntry entry = getOperationEntry(pathAddress, operationName);
return entry == null ? null : entry.getFlags();
}
@Override
public final AttributeAccess getAttributeAccess(final PathAddress address, final String attributeName) {
if (parent != null) {
RootInvocation ri = getRootInvocation();
return ri.root.getAttributeAccess(ri.pathAddress.append(address), attributeName);
}
// else we are the root
return getAttributeAccess(address.iterator(), attributeName);
}
abstract AttributeAccess getAttributeAccess(final ListIterator<PathElement> address, final String attributeName);
/**
* Get all the handlers at a specific address.
*
* @param address the address
* @param inherited true to include the inherited operations
* @return the handlers
*/
@Override
public final Map<String, OperationEntry> getOperationDescriptions(final PathAddress address, boolean inherited) {
if (parent != null) {
RootInvocation ri = getRootInvocation();
return ri.root.getOperationDescriptions(ri.pathAddress.append(address), inherited);
}
// else we are the root
Map<String, OperationEntry> providers = new TreeMap<String, OperationEntry>();
getOperationDescriptions(address.iterator(), providers, inherited);
return providers;
}
abstract void getOperationDescriptions(ListIterator<PathElement> iterator, Map<String, OperationEntry> providers, boolean inherited);
/**
* Get all the handlers at a specific address.
*
* @param address the address
* @param inherited true to include the inherited notifcations
* @return the handlers
*/
@Override
public final Map<String, NotificationEntry> getNotificationDescriptions(final PathAddress address, boolean inherited) {
if (parent != null) {
RootInvocation ri = getRootInvocation();
return ri.root.getNotificationDescriptions(ri.pathAddress.append(address), inherited);
}
// else we are the root
Map<String, NotificationEntry> providers = new TreeMap<String, NotificationEntry>();
getNotificationDescriptions(address.iterator(), providers, inherited);
return providers;
}
abstract void getNotificationDescriptions(ListIterator<PathElement> iterator, Map<String, NotificationEntry> providers, boolean inherited);
/** {@inheritDoc} */
@Override
public final DescriptionProvider getModelDescription(final PathAddress address) {
if (parent != null) {
RootInvocation ri = getRootInvocation();
return ri.root.getModelDescription(ri.pathAddress.append(address));
}
// else we are the root
return getModelDescription(address.iterator());
}
abstract DescriptionProvider getModelDescription(ListIterator<PathElement> iterator);
@Override
public final Set<String> getAttributeNames(final PathAddress address) {
if (parent != null) {
RootInvocation ri = getRootInvocation();
return ri.root.getAttributeNames(ri.pathAddress.append(address));
}
// else we are the root
return getAttributeNames(address.iterator());
}
abstract Set<String> getAttributeNames(ListIterator<PathElement> iterator);
@Override
public final Set<String> getChildNames(final PathAddress address) {
if (parent != null) {
RootInvocation ri = getRootInvocation();
return ri.root.getChildNames(ri.pathAddress.append(address));
}
// else we are the root
return getChildNames(address.iterator());
}
abstract Set<String> getChildNames(ListIterator<PathElement> iterator);
@Override
public final Set<PathElement> getChildAddresses(final PathAddress address){
if (parent != null) {
RootInvocation ri = getRootInvocation();
return ri.root.getChildAddresses(ri.pathAddress.append(address));
}
// else we are the root
return getChildAddresses(address.iterator());
}
abstract Set<PathElement> getChildAddresses(ListIterator<PathElement> iterator);
@Override
public final ProxyController getProxyController(final PathAddress address) {
if (parent != null) {
RootInvocation ri = getRootInvocation();
return ri.root.getProxyController(ri.pathAddress.append(address));
}
// else we are the root
return getProxyController(address.iterator());
}
abstract ProxyController getProxyController(ListIterator<PathElement> iterator);
@Override
public final Set<ProxyController> getProxyControllers(PathAddress address){
if (parent != null) {
RootInvocation ri = getRootInvocation();
return ri.root.getProxyControllers(ri.pathAddress.append(address));
}
// else we are the root
Set<ProxyController> controllers = new HashSet<ProxyController>();
getProxyControllers(address.iterator(), controllers);
return controllers;
}
abstract void getProxyControllers(ListIterator<PathElement> iterator, Set<ProxyController> controllers);
/** {@inheritDoc} */
@Override
public final ManagementResourceRegistration getOverrideModel(String name) {
Assert.checkNotNullParam("name", name);
if (parent == null) {
throw ControllerLogger.ROOT_LOGGER.cannotOverrideRootRegistration();
}
if (!PathElement.WILDCARD_VALUE.equals(valueString)) {
throw ControllerLogger.ROOT_LOGGER.cannotOverrideNonWildCardRegistration(valueString);
}
PathElement pe = PathElement.pathElement(parent.getKeyName(),name);
// TODO https://issues.jboss.org/browse/WFLY-2883
// ManagementResourceRegistration candidate = parent.getParent().getSubModel(PathAddress.pathAddress(pe));
// // We may have gotten back the wildcard reg; detect this by checking for allowing override
// return candidate.isAllowsOverride() ? null : candidate;
return parent.getParent().getSubModel(PathAddress.pathAddress(pe));
}
/** {@inheritDoc} */
@Override
public final ManagementResourceRegistration getSubModel(PathAddress address) {
return getSubRegistration(address);
}
final ManagementResourceRegistration getSubRegistration(PathAddress address) {
if (parent != null) {
RootInvocation ri = getRootInvocation();
return ri.root.getSubRegistration(ri.pathAddress.append(address));
}
// else we are the root
return getResourceRegistration(address.iterator());
}
abstract ManagementResourceRegistration getResourceRegistration(ListIterator<PathElement> iterator);
final String getLocationString() {
return getPathAddress().toCLIStyleString();
}
final void getInheritedOperations(final Map<String, OperationEntry> providers, boolean skipSelf) {
if (!skipSelf) {
getInheritedOperationEntries(providers);
}
if (parent != null) {
parent.getParent().getInheritedOperations(providers, false);
}
}
final void getInheritedNotifications(final Map<String, NotificationEntry> providers, boolean skipSelf) {
if (!skipSelf) {
getInheritedNotificationEntries(providers);
}
if (parent != null) {
parent.getParent().getInheritedNotifications(providers, false);
}
}
/** Gets whether this registration has an alternative wildcard registration */
boolean hasNoAlternativeWildcardRegistration() {
return parent == null || PathElement.WILDCARD_VALUE.equals(valueString) || !parent.getChildNames().contains(PathElement.WILDCARD_VALUE);
}
abstract void getInheritedOperationEntries(final Map<String, OperationEntry> providers);
abstract void getInheritedNotificationEntries(final Map<String, NotificationEntry> providers);
@Override
public final Set<RuntimeCapability> getCapabilities() {
if (parent != null) {
RootInvocation ri = getRootInvocation();
return ri.root.getCapabilities(ri.pathAddress.iterator());
}
// else we are the root
return getCapabilities(pathAddress.iterator());
}
abstract Set<RuntimeCapability> getCapabilities(ListIterator<PathElement> iterator);
@Override
public final Set<RuntimeCapability> getIncorporatingCapabilities() {
if (parent != null) {
RootInvocation ri = getRootInvocation();
return ri.root.getIncorporatingCapabilities(ri.pathAddress.iterator());
}
// else we are the root
return getIncorporatingCapabilities(pathAddress.iterator());
}
abstract Set<RuntimeCapability> getIncorporatingCapabilities(ListIterator<PathElement> iterator);
private RootInvocation getRootInvocation() {
RootInvocation result = null;
if (parent != null) {
synchronized (this) {
if (rootInvocation == null) {
NodeSubregistry ancestorSubregistry = parent;
AbstractResourceRegistration ancestorReg = this;
final List<PathElement> path = new ArrayList<PathElement>();
while (ancestorSubregistry != null) {
PathElement pe = PathElement.pathElement(ancestorSubregistry.getKeyName(), ancestorReg.valueString);
path.add(0, pe);
ancestorReg = ancestorSubregistry.getParent();
ancestorSubregistry = ancestorReg.parent;
}
PathAddress pa = PathAddress.pathAddress(path);
rootInvocation = new RootInvocation(ancestorReg, pa);
}
result = rootInvocation;
}
}
return result;
}
protected AbstractResourceRegistration getRootResourceRegistration() {
if (parent == null) {
return this;
}
RootInvocation invocation = getRootInvocation();
return invocation.root;
}
@Override
public void registerAlias(PathElement address, AliasEntry alias) {
RootInvocation rootInvocation = parent == null ? null : getRootInvocation();
AbstractResourceRegistration root = rootInvocation == null ? this : rootInvocation.root;
PathAddress myaddr = rootInvocation == null ? PathAddress.EMPTY_ADDRESS : rootInvocation.pathAddress;
PathAddress targetAddress = alias.getTarget().getPathAddress();
alias.setAddresses(targetAddress, myaddr.append(address));
AbstractResourceRegistration target = (AbstractResourceRegistration)root.getSubModel(alias.getTargetAddress());
if (target == null) {
throw ControllerLogger.ROOT_LOGGER.aliasTargetResourceRegistrationNotFound(alias.getTargetAddress());
}
registerAlias(address, alias, target);
}
protected abstract void registerAlias(PathElement address, AliasEntry alias, AbstractResourceRegistration target);
@Override
public boolean isAlias() {
//Overridden by AliasResourceRegistration
return false;
}
@Override
public AliasEntry getAliasEntry() {
//Overridden by AliasResourceRegistration
throw ControllerLogger.ROOT_LOGGER.resourceRegistrationIsNotAnAlias();
}
@Override
public PathAddress getPathAddress() {
return pathAddress;
}
@Override
public ImmutableManagementResourceRegistration getParent() {
return parent == null ? null : parent.getParent();
}
@Override
public Set<String> getOrderedChildTypes() {
if (parent != null) {
RootInvocation ri = getRootInvocation();
return ri.root.getOrderedChildTypes(ri.pathAddress.iterator());
}
// else we are the root
return getOrderedChildTypes(pathAddress.iterator());
}
abstract Set<String> getOrderedChildTypes(ListIterator<PathElement> iterator);
protected abstract void setOrderedChild(String key);
private static class RootInvocation {
final AbstractResourceRegistration root;
final PathAddress pathAddress;
RootInvocation(AbstractResourceRegistration root, PathAddress pathAddress) {
this.root = root;
this.pathAddress = pathAddress;
}
}
private static class OverrideDescriptionCombiner implements DescriptionProvider {
private final DescriptionProvider mainDescriptionProvider;
private final OverrideDescriptionProvider overrideDescriptionProvider;
private OverrideDescriptionCombiner(DescriptionProvider mainDescriptionProvider, OverrideDescriptionProvider overrideDescriptionProvider) {
this.mainDescriptionProvider = mainDescriptionProvider;
this.overrideDescriptionProvider = overrideDescriptionProvider;
}
@Override
public ModelNode getModelDescription(Locale locale) {
ModelNode result = mainDescriptionProvider.getModelDescription(locale);
ModelNode attrs = result.get(ModelDescriptionConstants.ATTRIBUTES);
for (Map.Entry<String, ModelNode> entry : overrideDescriptionProvider.getAttributeOverrideDescriptions(locale).entrySet()) {
attrs.get(entry.getKey()).set(entry.getValue());
}
ModelNode children = result.get(ModelDescriptionConstants.CHILDREN);
for (Map.Entry<String, ModelNode> entry : overrideDescriptionProvider.getChildTypeOverrideDescriptions(locale).entrySet()) {
children.get(entry.getKey()).set(entry.getValue());
}
return result;
}
}
}