/* * JBoss, Home of Professional Open Source. * Copyright 2012, 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.SUBSYSTEM; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import org.jboss.as.controller.ModelVersion; import org.jboss.as.controller.PathAddress; import org.jboss.as.controller.PathElement; import org.jboss.as.controller.transform.OperationResultTransformer; import org.jboss.as.controller.transform.OperationTransformer; import org.jboss.as.controller.transform.PathAddressTransformer; import org.jboss.as.controller.transform.ResourceTransformer; import org.jboss.as.controller.transform.TransformationContext; import org.jboss.as.controller.transform.TransformerEntry; import org.jboss.dmr.ModelNode; /** * Resolved/unversioned operation transformer registry. * * @author Emanuel Muckenhuber */ public class OperationTransformerRegistry { private final PathAddressTransformer pathAddressTransformer; private final ResourceTransformerEntry resourceTransformer; private final OperationTransformerEntry defaultTransformer; private final boolean placeholder; private volatile Map<String, SubRegistry> subRegistries; private volatile Map<String, OperationTransformerEntry> transformerEntries; private static final AtomicMapFieldUpdater<OperationTransformerRegistry, String, SubRegistry> subRegistriesUpdater = AtomicMapFieldUpdater.newMapUpdater(AtomicReferenceFieldUpdater.newUpdater(OperationTransformerRegistry.class, Map.class, "subRegistries")); private static final AtomicMapFieldUpdater<OperationTransformerRegistry, String, OperationTransformerEntry> entriesUpdater = AtomicMapFieldUpdater.newMapUpdater(AtomicReferenceFieldUpdater.newUpdater(OperationTransformerRegistry.class, Map.class, "transformerEntries")); protected OperationTransformerRegistry(final PathAddressTransformer pathAddressTransformer, final ResourceTransformerEntry resourceTransformer, final OperationTransformerEntry defaultTransformer, final boolean placeholder) { entriesUpdater.clear(this); subRegistriesUpdater.clear(this); this.defaultTransformer = defaultTransformer; this.resourceTransformer = resourceTransformer; this.pathAddressTransformer = pathAddressTransformer; this.placeholder = placeholder; } public TransformerEntry getTransformerEntry(final PathAddress address, PlaceholderResolver placeholderResolver) { return resolveTransformerEntry(address.iterator(), placeholderResolver); } protected TransformerEntry getTransformerEntry() { return new TransformerEntry() { @Override public PathAddressTransformer getPathTransformation() { return pathAddressTransformer; } @Override public ResourceTransformer getResourceTransformer() { return resourceTransformer.getTransformer(); } }; } /** * Resolve a resource transformer for a given address. * * @param address the address * @param placeholderResolver a placeholder resolver used to resolve children of a placeholder registration * @return the resource transformer */ public ResourceTransformerEntry resolveResourceTransformer(final PathAddress address, final PlaceholderResolver placeholderResolver) { return resolveResourceTransformer(address.iterator(), null, placeholderResolver); } /** * Resolve an operation transformer entry. * * @param address the address * @param operationName the operation name * @param placeholderResolver a placeholder resolver used to resolve children of a placeholder registration * @return the transformer entry */ public OperationTransformerEntry resolveOperationTransformer(final PathAddress address, final String operationName, PlaceholderResolver placeholderResolver) { final Iterator<PathElement> iterator = address.iterator(); final OperationTransformerEntry entry = resolveOperationTransformer(iterator, operationName, placeholderResolver); if(entry != null) { return entry; } // Default is forward unchanged return FORWARD; } /** * Merge a new subsystem from the global registration. * * @param registry the global registry * @param subsystemName the subsystem name * @param version the subsystem version */ public void mergeSubsystem(final GlobalTransformerRegistry registry, String subsystemName, ModelVersion version) { final PathElement element = PathElement.pathElement(SUBSYSTEM, subsystemName); registry.mergeSubtree(this, PathAddress.EMPTY_ADDRESS.append(element), version); } /** * Get a list of path transformers for a given address. * * @param address the path address * @param placeholderResolver a placeholder resolver used to resolve children of a placeholder registration * @return a list of path transformations */ public List<PathAddressTransformer> getPathTransformations(final PathAddress address, PlaceholderResolver placeholderResolver) { final List<PathAddressTransformer> list = new ArrayList<PathAddressTransformer>(); final Iterator<PathElement> iterator = address.iterator(); resolvePathTransformers(iterator, list, placeholderResolver); if(iterator.hasNext()) { while(iterator.hasNext()) { iterator.next(); list.add(PathAddressTransformer.DEFAULT); } } return list; } public OperationTransformerRegistry getChild(final PathAddress address) { final Iterator<PathElement> iterator = address.iterator(); return resolveChild(iterator); } public boolean isPlaceholder() { return placeholder; } private TransformerEntry resolveTransformerEntry(Iterator<PathElement> iterator, PlaceholderResolver placeholderResolver) { if(!iterator.hasNext()) { if (placeholder && placeholderResolver != null) { return placeholderResolver.resolveTransformerEntry(iterator); } else { return getTransformerEntry(); } } else { final PathElement element = iterator.next(); SubRegistry sub = subRegistriesUpdater.get(this, element.getKey()); if(sub == null) { return null; } final OperationTransformerRegistry registry = sub.get(element.getValue()); if(registry == null) { return null; } if (registry.placeholder) { return placeholderResolver != null ? placeholderResolver.resolveTransformerEntry(iterator) : registry.getTransformerEntry(); } else { return registry.resolveTransformerEntry(iterator, placeholderResolver); } } } ResourceTransformerEntry getResourceTransformer() { return resourceTransformer; } private OperationTransformerRegistry resolveChild(final Iterator<PathElement> iterator) { if(! iterator.hasNext()) { return this; } else { final PathElement element = iterator.next(); SubRegistry sub = subRegistriesUpdater.get(this, element.getKey()); if(sub == null) { return null; } return sub.get(element.getValue(), iterator); } } private void resolvePathTransformers(Iterator<PathElement> iterator, List<PathAddressTransformer> list, PlaceholderResolver placeholderResolver) { if(iterator.hasNext()) { final PathElement element = iterator.next(); SubRegistry sub = subRegistriesUpdater.get(this, element.getKey()); if(sub != null) { final OperationTransformerRegistry reg = sub.get(element.getValue()); if(reg != null) { list.add(reg.getPathAddressTransformer()); if (reg.isPlaceholder() && placeholderResolver != null) { placeholderResolver.resolvePathTransformers(iterator, list); } else { reg.resolvePathTransformers(iterator, list, placeholderResolver); } return; } } list.add(PathAddressTransformer.DEFAULT); return; } } PathAddressTransformer getPathAddressTransformer() { return pathAddressTransformer; } void registerTransformer(final PathAddress address, final String operationName, final OperationTransformer transformer) { registerTransformer(address.iterator(), operationName, new OperationTransformerEntry(transformer, false)); } public OperationTransformerEntry getDefaultTransformer() { return defaultTransformer; } Map<String, OperationTransformerEntry> getTransformers() { return entriesUpdater.get(this); } OperationTransformerRegistry createChildRegistry(final Iterator<PathElement> iterator, PathAddressTransformer pathAddressTransformer, ResourceTransformerEntry resourceTransformer, OperationTransformerEntry defaultTransformer, boolean placeholder) { if(!iterator.hasNext()) { return this; } else { final PathElement element = iterator.next(); return getOrCreate(element.getKey()).createChild(iterator, element.getValue(), pathAddressTransformer, resourceTransformer, defaultTransformer, placeholder); } } void registerTransformer(final Iterator<PathElement> iterator, String operationName, OperationTransformerEntry entry) { if(! iterator.hasNext()) { final OperationTransformerEntry existing = entriesUpdater.putIfAbsent(this, operationName, entry); if(existing != null) { throw new IllegalStateException("duplicate transformer " + operationName); } } else { final PathElement element = iterator.next(); getOrCreate(element.getKey()).registerTransformer(iterator, element.getValue(), operationName, entry); } } private ResourceTransformerEntry resolveResourceTransformer(final Iterator<PathElement> iterator, final ResourceTransformerEntry inherited, final PlaceholderResolver placeholderResolver) { if(! iterator.hasNext()) { if(resourceTransformer == null) { return inherited; } return resourceTransformer; } else { final ResourceTransformerEntry inheritedEntry = resourceTransformer.inherited ? resourceTransformer : inherited; final PathElement element = iterator.next(); final String key = element.getKey(); SubRegistry registry = subRegistriesUpdater.get(this, key); if(registry == null) { return inherited; } return registry.resolveResourceTransformer(iterator, element.getValue(), inheritedEntry, placeholderResolver); } } private OperationTransformerEntry resolveOperationTransformer(final Iterator<PathElement> iterator, final String operationName, final PlaceholderResolver placeholderResolver) { if(! iterator.hasNext()) { if (placeholder && placeholderResolver != null) { return placeholderResolver.resolveOperationTransformer(iterator, operationName); } else { final OperationTransformerEntry entry = entriesUpdater.get(this, operationName); if(entry == null) { return defaultTransformer; } return entry; } } else { final PathElement element = iterator.next(); final String key = element.getKey(); SubRegistry sub = subRegistriesUpdater.get(this, key); OperationTransformerEntry entry = null; if(sub != null) { final OperationTransformerRegistry registry = sub.get(element.getValue()); if (registry != null) { if (registry.placeholder) { entry = placeholderResolver != null ? placeholderResolver.resolveOperationTransformer(iterator, operationName) : registry.getDefaultTransformer(); } else { entry = registry.resolveOperationTransformer(iterator, operationName, placeholderResolver); } } if (entry != null) { return entry; } } //Look for inherited entries entry = entriesUpdater.get(this, operationName); if (entry != null && entry.isInherited()) { return entry; } entry = defaultTransformer; if (entry != null && entry.isInherited()) { return entry; } return null; } } private SubRegistry getOrCreate(final String key) { for (;;) { final Map<String, SubRegistry> subRegistries = subRegistriesUpdater.get(this); SubRegistry registry = subRegistries.get(key); if (registry != null) { return registry; } else { registry = new SubRegistry(); if(subRegistriesUpdater.putAtomic(this, key, registry, subRegistries)) { return registry; } } return registry; } } private static class SubRegistry { private static final AtomicMapFieldUpdater<SubRegistry, String, OperationTransformerRegistry> childrenUpdater = AtomicMapFieldUpdater.newMapUpdater(AtomicReferenceFieldUpdater.newUpdater(SubRegistry.class, Map.class, "entries")); private volatile Map<String, OperationTransformerRegistry> entries; private SubRegistry() { childrenUpdater.clear(this); } private OperationTransformerRegistry createChild(Iterator<PathElement> iterator, String value, final PathAddressTransformer pathAddressTransformer, ResourceTransformerEntry resourceTransformer, OperationTransformerEntry defaultTransformer, boolean placeholder) { if(! iterator.hasNext()) { return create(value, pathAddressTransformer, resourceTransformer, defaultTransformer, placeholder); } else { OperationTransformerRegistry entry = get(value); if(entry == null) { entry = create(value, PathAddressTransformer.DEFAULT, GlobalTransformerRegistry.RESOURCE_TRANSFORMER, FORWARD, placeholder); } return entry.createChildRegistry(iterator, pathAddressTransformer, resourceTransformer, defaultTransformer, placeholder); } } private void registerTransformer(Iterator<PathElement> iterator, String value, String operationName, OperationTransformerEntry entry) { get(value).registerTransformer(iterator, operationName, entry); } private OperationTransformerRegistry get(final String value) { OperationTransformerRegistry entry = childrenUpdater.get(this, value); if(entry == null) { entry = childrenUpdater.get(this, "*"); if(entry == null) { return null; } } return entry; } private OperationTransformerRegistry get(final String value, Iterator<PathElement> iterator) { OperationTransformerRegistry entry = childrenUpdater.get(this, value); if(entry == null) { entry = childrenUpdater.get(this, "*"); if(entry == null) { return null; } } return entry.resolveChild(iterator); } private OperationTransformerRegistry create(final String value, final PathAddressTransformer pathAddressTransformer, final ResourceTransformerEntry resourceTransformer, final OperationTransformerEntry defaultTransformer, boolean placeholder) { for(;;) { final Map<String, OperationTransformerRegistry> entries = childrenUpdater.get(this); OperationTransformerRegistry entry = entries.get(value); if(entry != null) { return entry; } else { entry = new OperationTransformerRegistry(pathAddressTransformer, resourceTransformer, defaultTransformer, placeholder); if(childrenUpdater.putAtomic(this, value, entry, entries)) { return entry; } } } } private ResourceTransformerEntry resolveResourceTransformer(Iterator<PathElement> iterator, String value, ResourceTransformerEntry inheritedEntry, PlaceholderResolver placeholderResolver) { final OperationTransformerRegistry registry = get(value); if(registry == null) { return inheritedEntry; } return registry.resolveResourceTransformer(iterator, inheritedEntry, placeholderResolver); } } public static class ResourceTransformerEntry { private final ResourceTransformer transformer; private final boolean inherited; public ResourceTransformerEntry(ResourceTransformer transformer, boolean inherited) { this.transformer = transformer; this.inherited = inherited; } public ResourceTransformer getTransformer() { return transformer; } public boolean isInherited() { return inherited; } } public static class OperationTransformerEntry { final OperationTransformer transformer; final boolean inherited; public OperationTransformerEntry(OperationTransformer transformer, boolean inherited) { this.transformer = transformer; this.inherited = inherited; } public OperationTransformer getTransformer() { return transformer; } public boolean isInherited() { return inherited; } } static final OperationTransformer FORWARD_TRANSFORMER = new OperationTransformer() { @Override public TransformedOperation transformOperation(TransformationContext context, PathAddress address, ModelNode operation) { return new TransformedOperation(operation, OperationResultTransformer.ORIGINAL_RESULT); } }; static final OperationTransformer DISCARD_TRANSFORMER = OperationTransformer.DISCARD; public static final OperationTransformerEntry DISCARD = new OperationTransformerEntry(DISCARD_TRANSFORMER, true); public static final OperationTransformerEntry FORWARD = new OperationTransformerEntry(FORWARD_TRANSFORMER, false); /** * An extra resolver to be used for {@link OperationTransformerRegistry} entries where {@code placeholder==true}. These placeholder entries transformers should create * a new {@link org.jboss.as.controller.transform.TransformationTarget} containing the {@code PlaceholderResolver} and resolve the children themselves. * Note that if a place holder resolver is used at a given resource address, this takes precedence over the normal transformer registry, so all children * must use the placeholders. */ public interface PlaceholderResolver { /** * Resolves a resource transformer from the relative address of the current {@code OperationTransformerRegistry} entry * * @param iterator an iterator of the path elements of the resource we want to transform. On the initial call, this will be at the address of the placeholder entry * @param operationName the name of the operation transformer to resolve * @return the operation transformer, or {@code null} if not found */ OperationTransformerEntry resolveOperationTransformer(final Iterator<PathElement> iterator, final String operationName); /** * Adds path address transformers to the list for the relative address and below of the current {@code OperationTransformerRegistry} entry * * @param iterator an iterator of the path elements of the resource we want to transform. On the initial call, this will be at the address of the placeholder entry * @param list the list of path address transformers to add the results to */ void resolvePathTransformers(Iterator<PathElement> iterator, List<PathAddressTransformer> list); /** * Resolves a {@link TransformerEntry} from the relative address of the current {@code OperationTransformerRegistry} entry * @param iterator an iterator of the path elements of the resource we want to transform. On the initial call, this will be at the address of the placeholder entry * @return the transformer entry, or {@code null} if not found */ TransformerEntry resolveTransformerEntry(Iterator<PathElement> iterator); } }