/* * 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 static org.jboss.as.controller.descriptions.ModelDescriptionConstants.NOTIFICATION; import java.util.ArrayList; import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Set; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.jboss.as.controller.AttributeDefinition; import org.jboss.as.controller.CapabilityRegistry; import org.jboss.as.controller.NotificationDefinition; 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.ProcessType; import org.jboss.as.controller.ProxyController; import org.jboss.as.controller.ResourceDefinition; import org.jboss.as.controller.access.management.AccessConstraintDefinition; import org.jboss.as.controller.access.management.AccessConstraintUtilizationRegistry; import org.jboss.as.controller.capability.RuntimeCapability; import org.jboss.as.controller.descriptions.DescriptionProvider; import org.jboss.as.controller.logging.ControllerLogger; import org.jboss.as.controller.registry.AttributeAccess.AccessType; import org.jboss.as.controller.registry.AttributeAccess.Storage; import org.wildfly.common.Assert; final class ConcreteResourceRegistration extends AbstractResourceRegistration { private Map<String, NodeSubregistry> children; private Map<String, OperationEntry> operations; private Map<String, NotificationEntry> notifications; private final ResourceDefinition resourceDefinition; private final List<AccessConstraintDefinition> accessConstraintDefinitions; // We assume at least 2 attrs, so just instantiate a hash map private final Map<String, AttributeAccess> attributes = new HashMap<>(); private Set <String> orderedChildTypes; private boolean runtimeOnly; private final boolean ordered; private final AccessConstraintUtilizationRegistry constraintUtilizationRegistry; private final CapabilityRegistry capabilityRegistry; private Set<RuntimeCapability> capabilities; private Set<RuntimeCapability> incorporatingCapabilities; private final Lock readLock; private final Lock writeLock; /** * Can be {@code null}. In that case, the MMR will always register metrics. */ final ProcessType processType; ConcreteResourceRegistration(final String valueString, final NodeSubregistry parent, final ResourceDefinition definition, final AccessConstraintUtilizationRegistry constraintUtilizationRegistry, final boolean ordered, CapabilityRegistry capabilityRegistry, ProcessType processType) { super(valueString, parent); this.constraintUtilizationRegistry = constraintUtilizationRegistry; this.capabilityRegistry = capabilityRegistry; this.resourceDefinition = definition; this.processType = processType; this.runtimeOnly = definition.isRuntime(); this.accessConstraintDefinitions = buildAccessConstraints(); this.ordered = ordered; if (parent == null) { // For a root MRR we expect concurrent reads in critical performance code, i.e. boot // So we use a read-write lock ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); this.readLock = rwLock.readLock(); this.writeLock = rwLock.writeLock(); } else { // For non-root MRRs we don't expect much in the way of concurrent reads in performance // critical situations, so we want lock/unlock to be as simple and fast as possible // So we just use a single non-r/w lock for both reads and writes this.readLock = this.writeLock = new ReentrantLock(); } } void beginInitialization() { writeLock.lock(); } void initialized() { writeLock.unlock(); } @Override public int getMaxOccurs() { return resourceDefinition.getMaxOccurs(); } @Override public int getMinOccurs() { return resourceDefinition.getMinOccurs(); } @Override public boolean isRuntimeOnly() { checkPermission(); readLock.lock(); try { return runtimeOnly; } finally { readLock.unlock(); } } @Override public void setRuntimeOnly(final boolean runtimeOnly) { checkPermission(); writeLock.lock(); try { this.runtimeOnly = runtimeOnly; } finally { writeLock.unlock(); } } @Override public boolean isRemote() { checkPermission(); return false; } @Override public boolean isOrderedChildResource() { return ordered; } @Override Set<String> getOrderedChildTypes(ListIterator<PathElement> iterator) { if (iterator.hasNext()) { final PathElement next = iterator.next(); final NodeSubregistry subregistry = getSubregistry(next.getKey()); if (subregistry == null) { return Collections.emptySet(); } return subregistry.getOrderedChildTypes(iterator, next.getValue()); } else { checkPermission(); readLock.lock(); try { return orderedChildTypes == null ? Collections.emptySet() : new HashSet<>(orderedChildTypes); } finally { readLock.unlock(); } } } @Override public List<AccessConstraintDefinition> getAccessConstraints() { checkPermission(); return accessConstraintDefinitions; } private List<AccessConstraintDefinition> buildAccessConstraints() { AbstractResourceRegistration reg = this; List<AccessConstraintDefinition> list = new ArrayList<AccessConstraintDefinition>(); while (reg != null) { reg.addAccessConstraints(list); NodeSubregistry parent = reg.getParentSubRegistry(); reg = parent == null ? null : parent.getParent(); } return Collections.unmodifiableList(list); } @Override void addAccessConstraints(List<AccessConstraintDefinition> list) { list.addAll(resourceDefinition.getAccessConstraints()); } @Override public ManagementResourceRegistration registerSubModel(final ResourceDefinition resourceDefinition) { Assert.checkNotNullParam("resourceDefinition", resourceDefinition); final PathElement address = resourceDefinition.getPathElement(); if (address == null) { throw ControllerLogger.ROOT_LOGGER.cannotRegisterSubmodelWithNullPath(); } if (isRuntimeOnly() && !resourceDefinition.isRuntime()) { throw ControllerLogger.ROOT_LOGGER.cannotRegisterSubmodel(); } final ManagementResourceRegistration existing = getSubRegistration(PathAddress.pathAddress(address)); if (existing != null && existing.getPathAddress().getLastElement().getValue().equals(address.getValue())) { throw ControllerLogger.ROOT_LOGGER.nodeAlreadyRegistered(existing.getPathAddress().toCLIStyleString()); } final NodeSubregistry child = getOrCreateSubregistry(address.getKey()); return child.registerChild(address.getValue(), resourceDefinition); } @Override public void registerOperationHandler(OperationDefinition definition, OperationStepHandler handler, boolean inherited) { checkPermission(); String opName = definition.getName(); OperationEntry entry = new OperationEntry(definition, handler, inherited); writeLock.lock(); try { if (operations == null) { operations = new HashMap<>(); } else if (operations.containsKey(opName)) { throw alreadyRegistered("operation handler", opName); } operations.put(opName, entry); if (constraintUtilizationRegistry != null) { for (AccessConstraintDefinition acd : definition.getAccessConstraints()) { constraintUtilizationRegistry.registerAccessConstraintOperationUtilization(acd.getKey(), getPathAddress(), opName); } } } finally { writeLock.unlock(); } } public void unregisterSubModel(final PathElement address) throws IllegalArgumentException { writeLock.lock(); try { final NodeSubregistry subregistry = getSubregistry(address.getKey()); if (subregistry != null) { //we remove also children, effectively doing recursive delete Set<PathElement> childAddresses = getChildAddresses(PathAddress.pathAddress(address)); if (childAddresses != null) { ManagementResourceRegistration registration = subregistry.getResourceRegistration(PathAddress.EMPTY_ADDRESS.iterator(), address.getValue()); if(!registration.isAlias()) { for (PathElement a : childAddresses) { registration.unregisterSubModel(a); } } } subregistry.unregisterSubModel(address.getValue()); } if (constraintUtilizationRegistry != null) { constraintUtilizationRegistry.unregisterAccessConstraintUtilizations(getPathAddress().append(address)); } } finally { writeLock.unlock(); } } @Override OperationEntry getOperationEntry(final ListIterator<PathElement> iterator, final String operationName, OperationEntry inherited) { if (iterator.hasNext()) { final NodeSubregistry subregistry; final OperationEntry inheritance; final PathElement next = iterator.next(); readLock.lock(); try { subregistry = children == null ? null : children.get(next.getKey()); if (subregistry == null) { return null; } OperationEntry ourInherited = getInheritableOperationEntryLocked(operationName); inheritance = ourInherited == null ? inherited : ourInherited; } finally { readLock.unlock(); } return subregistry.getOperationEntry(iterator, next.getValue(), operationName, inheritance); } else { checkPermission(); final OperationEntry entry; readLock.lock(); try { entry = operations == null ? null : operations.get(operationName); } finally { readLock.unlock(); } return entry == null ? inherited : entry; } } @Override OperationEntry getInheritableOperationEntry(final String operationName) { checkPermission(); readLock.lock(); try { return getInheritableOperationEntryLocked(operationName); } finally { readLock.unlock(); } } // Only call with the read lock held private OperationEntry getInheritableOperationEntryLocked(final String operationName) { final OperationEntry entry = operations == null ? null : operations.get(operationName); if (entry != null && entry.isInherited()) { return entry; } return null; } @Override void getOperationDescriptions(final ListIterator<PathElement> iterator, final Map<String, OperationEntry> providers, final boolean inherited) { if (!iterator.hasNext() ) { checkPermission(); readLock.lock(); try { if (operations != null) { providers.putAll(operations); } } finally { readLock.unlock(); } if (inherited) { getInheritedOperations(providers, true); } return; } final PathElement next = iterator.next(); try { final String key = next.getKey(); final NodeSubregistry subregistry = getSubregistry(key); if (subregistry != null) { subregistry.getHandlers(iterator, next.getValue(), providers, inherited); } } finally { iterator.previous(); } } @Override void getInheritedOperationEntries(final Map<String, OperationEntry> providers) { checkPermission(); readLock.lock(); try { if (operations != null) { for (final Map.Entry<String, OperationEntry> entry : operations.entrySet()) { if (entry.getValue().isInherited() && !providers.containsKey(entry.getKey())) { providers.put(entry.getKey(), entry.getValue()); } } } } finally { readLock.unlock(); } } @Override public void unregisterOperationHandler(final String operationName) { checkPermission(); writeLock.lock(); try { if (operations == null || operations.remove(operationName) == null) { throw operationNotRegisteredException(operationName, resourceDefinition.getPathElement()); } } finally { writeLock.unlock(); } } @Override public void registerReadWriteAttribute(final AttributeDefinition definition, final OperationStepHandler readHandler, final OperationStepHandler writeHandler) { assert definition.getUndefinedMetricValue() == null : "Attributes cannot have undefined metric value set"; checkPermission(); if (!isAttributeRegistrationAllowed(definition)) { return; } final EnumSet<AttributeAccess.Flag> flags = definition.getFlags(); AttributeAccess.Storage storage = (flags != null && flags.contains(AttributeAccess.Flag.STORAGE_RUNTIME)) ? Storage.RUNTIME : Storage.CONFIGURATION; AttributeAccess aa = new AttributeAccess(AccessType.READ_WRITE, storage, readHandler, writeHandler, definition, flags); storeAttribute(definition, aa); } @Override public void registerReadOnlyAttribute(final AttributeDefinition definition, final OperationStepHandler readHandler) { assert definition.getUndefinedMetricValue() == null : "Attributes cannot have undefined metric value set"; checkPermission(); if (!isAttributeRegistrationAllowed(definition)) { return; } final EnumSet<AttributeAccess.Flag> flags = definition.getFlags(); AttributeAccess.Storage storage = (flags != null && flags.contains(AttributeAccess.Flag.STORAGE_RUNTIME)) ? Storage.RUNTIME : Storage.CONFIGURATION; AttributeAccess aa = new AttributeAccess(AccessType.READ_ONLY, storage, readHandler, null, definition, flags); storeAttribute(definition, aa); } @Override public void unregisterAttribute(String attributeName) { checkPermission(); writeLock.lock(); try { attributes.remove(attributeName); } finally { writeLock.unlock(); } } @Override public void registerNotification(NotificationDefinition notification, boolean inherited) { checkPermission(); String type = notification.getType(); NotificationEntry entry = new NotificationEntry(notification.getDescriptionProvider(), inherited); writeLock.lock(); try { if (notifications == null) { notifications = Collections.singletonMap(type, entry); } else { if (notifications.containsKey(type)) { throw alreadyRegistered(NOTIFICATION, type); } if (notifications.size() == 1) { notifications = new HashMap<>(notifications); } notifications.put(type, entry); } } finally { writeLock.unlock(); } } @Override public void registerNotification(NotificationDefinition notification) { registerNotification(notification, false); } @Override public void unregisterNotification(String notificationType) { checkPermission(); writeLock.lock(); try { if (notifications != null) { notifications.remove(notificationType); } } finally { writeLock.unlock(); } } @Override public void registerMetric(AttributeDefinition definition, OperationStepHandler metricHandler) { assert assertMetricValues(definition); //The real message will be in an assertion thrown by assertMetricValues checkPermission(); if (isAttributeRegistrationAllowed(definition)) { AttributeAccess aa = new AttributeAccess(AccessType.METRIC, AttributeAccess.Storage.RUNTIME, metricHandler, null, definition, definition.getFlags()); storeAttribute(definition, aa); } } /** * Metrics and runtime attributes are always registered in the MMR only for normal server. * If they are flagged with {@link AttributeAccess.Flag#STORAGE_RUNTIME} and * {@link AttributeAccess.Flag#RUNTIME_SERVICE_NOT_REQUIRED}, they are registered regardless of the process type. */ private boolean isAttributeRegistrationAllowed(AttributeDefinition definition) { if (processType == null) { return true; } boolean runtime = definition.getFlags().contains(AttributeAccess.Flag.STORAGE_RUNTIME); if (!runtime) { return true; } boolean runtimeServiceNotRequired = definition.getFlags().contains(AttributeAccess.Flag.RUNTIME_SERVICE_NOT_REQUIRED); if (runtimeServiceNotRequired) { return true; } return processType.isServer(); } private void storeAttribute(AttributeDefinition definition, AttributeAccess aa) { String attributeName = definition.getName(); writeLock.lock(); try { if (attributes.containsKey(attributeName)) { throw alreadyRegistered("attribute", attributeName); } attributes.put(attributeName, aa); registerAttributeAccessConstraints(definition); } finally { writeLock.unlock(); } } private boolean assertMetricValues(AttributeDefinition definition) { if (!definition.isRequired() && definition.getUndefinedMetricValue() != null) { assert false : "Nillable metric has an undefined metric value for '" + definition.getName() + "'"; } // BES 2015/08/28 The WFCORE-831 spec does not require this assertion. The requirement is that read-attribute // not return undefined, but AttributeDefinition.getUndefinedMetricValue() is not the only way to achieve this. // The read-attribute handler can simply always work. // if (!definition.isAllowNull() && definition.getUndefinedMetricValue() == null) { // assert false : "Non-nillable metric does not have an undefined metric value for '" + definition.getName() + "'"; // } if (definition.getDefaultValue() != null) { assert false : "Metrics cannot have a default value for '" + definition.getName() + "'"; } return true; } private void registerAttributeAccessConstraints(AttributeDefinition ad) { if (constraintUtilizationRegistry != null) { for (AccessConstraintDefinition acd : ad.getAccessConstraints()) { constraintUtilizationRegistry.registerAccessConstraintAttributeUtilization(acd.getKey(), getPathAddress(), ad.getName()); } } } @Override void getNotificationDescriptions(final ListIterator<PathElement> iterator, final Map<String, NotificationEntry> providers, final boolean inherited) { if (!iterator.hasNext() ) { checkPermission(); readLock.lock(); try { if (notifications != null) { providers.putAll(notifications); } } finally { readLock.unlock(); } if (inherited) { getInheritedNotifications(providers, true); } return; } final PathElement next = iterator.next(); try { final String key = next.getKey(); final NodeSubregistry subregistry = getSubregistry(key); if (subregistry != null) { subregistry.getNotificationDescriptions(iterator, next.getValue(), providers, inherited); } } finally { iterator.previous(); } } private NodeSubregistry getSubregistry(String key) { readLock.lock(); try { return children == null ? null : children.get(key); } finally { readLock.unlock(); } } @Override void getInheritedNotificationEntries(final Map<String, NotificationEntry> providers) { checkPermission(); readLock.lock(); try { if (notifications != null) { for (final Map.Entry<String, NotificationEntry> entry : notifications.entrySet()) { if (entry.getValue().isInherited() && !providers.containsKey(entry.getKey())) { providers.put(entry.getKey(), entry.getValue()); } } } } finally { readLock.unlock(); } } @Override Set<RuntimeCapability> getCapabilities(ListIterator<PathElement> iterator) { if (iterator.hasNext()) { final PathElement next = iterator.next(); final NodeSubregistry subregistry = getSubregistry(next.getKey()); if (subregistry == null) { return Collections.emptySet(); } return subregistry.getCapabilities(iterator, next.getValue()); } else { checkPermission(); readLock.lock(); try { return capabilities == null ? Collections.emptySet() : Collections.unmodifiableSet(capabilities); } finally { readLock.unlock(); } } } @Override Set<RuntimeCapability> getIncorporatingCapabilities(ListIterator<PathElement> iterator) { if (iterator.hasNext()) { final PathElement next = iterator.next(); final NodeSubregistry subregistry = getSubregistry(next.getKey()); if (subregistry == null) { return Collections.emptySet(); } return subregistry.getIncorporatingCapabilities(iterator, next.getValue()); } else { checkPermission(); readLock.lock(); try { Set<RuntimeCapability> result; if (incorporatingCapabilities != null) { result = incorporatingCapabilities; } else if (capabilities != null && !capabilities.isEmpty()) { result = Collections.emptySet(); } else { result = null; } return result; } finally { readLock.unlock(); } } } @Override public void registerProxyController(final PathElement address, final ProxyController controller) throws IllegalArgumentException { final ManagementResourceRegistration existing = getSubRegistration(PathAddress.pathAddress(address)); if (existing != null && existing.getPathAddress().getLastElement().getValue().equals(address.getValue())) { throw ControllerLogger.ROOT_LOGGER.nodeAlreadyRegistered(existing.getPathAddress().toCLIStyleString()); } getOrCreateSubregistry(address.getKey()).registerProxyController(address.getValue(), controller); } @Override public void unregisterProxyController(final PathElement address) throws IllegalArgumentException { final NodeSubregistry subregistry = getSubregistry(address.getKey()); if (subregistry != null) { subregistry.unregisterProxyController(address.getValue()); } } @Override public void registerAlias(PathElement address, AliasEntry alias, AbstractResourceRegistration target) { getOrCreateSubregistry(address.getKey()).registerAlias(address.getValue(), alias, target); } @Override public void unregisterAlias(PathElement address) { final NodeSubregistry subregistry = getSubregistry(address.getKey()); if (subregistry != null) { subregistry.unregisterAlias(address.getValue()); } } @Override public void registerCapability(RuntimeCapability capability) { writeLock.lock(); try { if (capabilities == null) { capabilities = new HashSet<>(); } capabilities.add(capability); if (capabilityRegistry != null) { capabilityRegistry.registerPossibleCapability(capability, getPathAddress()); } } finally { writeLock.unlock(); } } @Override public void registerIncorporatingCapabilities(Set<RuntimeCapability> capabilities) { writeLock.lock(); try { if (capabilities == null) { incorporatingCapabilities = null; } else if (capabilities.isEmpty()) { incorporatingCapabilities = Collections.emptySet(); } else { incorporatingCapabilities = Collections.unmodifiableSet(new HashSet<>(capabilities)); } } finally { writeLock.unlock(); } } NodeSubregistry getOrCreateSubregistry(final String key) { writeLock.lock(); try { final NodeSubregistry subregistry = children == null ? null : children.get(key); if (subregistry != null) { return subregistry; } else { checkPermission(); final NodeSubregistry newRegistry = new NodeSubregistry(key, this, constraintUtilizationRegistry, capabilityRegistry); if (children == null) { children = Collections.singletonMap(key, newRegistry); } else { if (children.size() == 1) { children = new HashMap<>(children); } children.put(key, newRegistry); } return newRegistry; } } finally { writeLock.unlock(); } } @Override DescriptionProvider getModelDescription(final ListIterator<PathElement> iterator) { if (iterator.hasNext()) { final PathElement next = iterator.next(); final NodeSubregistry subregistry = getSubregistry(next.getKey()); if (subregistry == null) { return null; } return subregistry.getModelDescription(iterator, next.getValue()); } else { checkPermission(); return resourceDefinition.getDescriptionProvider(this); } } @Override Set<String> getAttributeNames(final ListIterator<PathElement> iterator) { if (iterator.hasNext()) { final PathElement next = iterator.next(); final NodeSubregistry subregistry = getSubregistry(next.getKey()); if (subregistry == null) { return Collections.emptySet(); } return subregistry.getAttributeNames(iterator, next.getValue()); } else { checkPermission(); synchronized (this) { return new HashSet<>(attributes.keySet()); } } } @Override AttributeAccess getAttributeAccess(final ListIterator<PathElement> iterator, final String attributeName) { if (iterator.hasNext()) { final PathElement next = iterator.next(); final NodeSubregistry subregistry = getSubregistry(next.getKey()); if (subregistry == null) { return null; } return subregistry.getAttributeAccess(iterator, next.getValue(), attributeName); } else { checkPermission(); readLock.lock(); try { return attributes.get(attributeName); } finally { readLock.unlock(); } } } @Override Set<String> getChildNames(final ListIterator<PathElement> iterator) { if (iterator.hasNext()) { final PathElement next = iterator.next(); final NodeSubregistry subregistry = getSubregistry(next.getKey()); if (subregistry == null) { return Collections.emptySet(); } return subregistry.getChildNames(iterator, next.getValue()); } else { checkPermission(); readLock.lock(); try { if (children != null) { return Collections.unmodifiableSet(children.keySet()); } return Collections.emptySet(); } finally { readLock.unlock(); } } } @Override Set<PathElement> getChildAddresses(final ListIterator<PathElement> iterator) { if (iterator.hasNext()) { final PathElement next = iterator.next(); final NodeSubregistry subregistry = getSubregistry(next.getKey()); if (subregistry == null) { return Collections.emptySet(); } return subregistry.getChildAddresses(iterator, next.getValue()); } else { checkPermission(); readLock.lock(); try { if (children != null) { final Set<PathElement> elements = new HashSet<PathElement>(); for (final Map.Entry<String, NodeSubregistry> entry : children.entrySet()) { for (final String entryChild : entry.getValue().getChildNames()) { elements.add(PathElement.pathElement(entry.getKey(), entryChild)); } } return elements; } } finally { readLock.unlock(); } return Collections.emptySet(); } } @Override ProxyController getProxyController(ListIterator<PathElement> iterator) { if (iterator.hasNext()) { final PathElement next = iterator.next(); final NodeSubregistry subregistry = getSubregistry(next.getKey()); if (subregistry == null) { return null; } return subregistry.getProxyController(iterator, next.getValue()); } else { return null; } } @Override void getProxyControllers(ListIterator<PathElement> iterator, Set<ProxyController> controllers) { if (iterator.hasNext()) { final PathElement next = iterator.next(); final NodeSubregistry subregistry = getSubregistry(next.getKey()); if (subregistry == null) { return; } if (next.isWildcard()) { subregistry.getProxyControllers(iterator, null, controllers); } else if (next.isMultiTarget()) { for(final String value : next.getSegments()) { subregistry.getProxyControllers(iterator, value, controllers); } } else { subregistry.getProxyControllers(iterator, next.getValue(), controllers); } } else { readLock.lock(); try { if (children != null) { for (NodeSubregistry subregistry : children.values()) { subregistry.getProxyControllers(iterator, null, controllers); } } } finally { readLock.unlock(); } } } @Override ManagementResourceRegistration getResourceRegistration(ListIterator<PathElement> iterator) { if (! iterator.hasNext()) { checkPermission(); return this; } else { final PathElement address = iterator.next(); final NodeSubregistry subregistry = getSubregistry(address.getKey()); if (subregistry != null) { return subregistry.getResourceRegistration(iterator, address.getValue()); } else { return null; } } } private IllegalArgumentException alreadyRegistered(final String type, final String name) { return ControllerLogger.ROOT_LOGGER.alreadyRegistered(type, name, getLocationString()); } private IllegalArgumentException operationNotRegisteredException(String op, PathElement address) { return ControllerLogger.ROOT_LOGGER.operationNotRegisteredException(op, PathAddress.pathAddress(address)); } @Override public AliasEntry getAliasEntry() { checkPermission(); return null; } @Override protected void setOrderedChild(String type) { writeLock.lock(); try { if (orderedChildTypes == null) { orderedChildTypes = Collections.singleton(type); } else { if (orderedChildTypes.size() == 1) { orderedChildTypes = new HashSet<>(orderedChildTypes); } if (!orderedChildTypes.add(type)) { throw alreadyRegistered("Ordered child", type); } } } finally { writeLock.unlock(); } } }