/* * 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 java.util.Collections; import java.util.Iterator; 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.logging.ControllerLogger; import org.jboss.as.controller.transform.OperationTransformer; import org.jboss.as.controller.transform.PathAddressTransformer; import org.jboss.as.controller.transform.ResourceTransformer; /** * Versioned operation transformer registry. * * @author Emanuel Muckenhuber */ public class GlobalTransformerRegistry { private volatile Map<String, SubRegistry> subRegistries; private volatile Map<ModelVersion, OperationTransformerRegistry> versionedRegistries; private static final AtomicMapFieldUpdater<GlobalTransformerRegistry, String, SubRegistry> subRegistriesUpdater = AtomicMapFieldUpdater.newMapUpdater(AtomicReferenceFieldUpdater.newUpdater(GlobalTransformerRegistry.class, Map.class, "subRegistries")); private static final AtomicMapFieldUpdater<GlobalTransformerRegistry, ModelVersion, OperationTransformerRegistry> registryUpdater = AtomicMapFieldUpdater.newMapUpdater(AtomicReferenceFieldUpdater.newUpdater(GlobalTransformerRegistry.class, Map.class, "versionedRegistries")); public GlobalTransformerRegistry() { registryUpdater.clear(this); subRegistriesUpdater.clear(this); } /** * Discard an operation. * * @param address the operation handler address * @param major the major version * @param minor the minor version * @param operationName the operation name */ public void discardOperation(final PathAddress address, int major, int minor, final String operationName) { registerTransformer(address.iterator(), ModelVersion.create(major, minor), operationName, OperationTransformerRegistry.DISCARD); } /** * Discard an operation. * * @param address the operation handler address * @param version the model version * @param operationName the operation name */ public void discardOperation(final PathAddress address, ModelVersion version, final String operationName) { registerTransformer(address.iterator(), version, operationName, OperationTransformerRegistry.DISCARD); } /** * Register an operation transformer. * * @param address the operation handler address * @param major the major version * @param minor the minor version * @param operationName the operation name * @param transformer the operation transformer */ public void registerTransformer(final PathAddress address, int major, int minor, String operationName, OperationTransformer transformer) { registerTransformer(address.iterator(), ModelVersion.create(major, minor), operationName, new OperationTransformerRegistry.OperationTransformerEntry(transformer, false)); } public void createDiscardingChildRegistry(final PathAddress address, final ModelVersion version) { createChildRegistry(address.iterator(), version, PathAddressTransformer.DEFAULT, DISCARD, OperationTransformerRegistry.DISCARD, false); } public void createChildRegistry(final PathAddress address, final ModelVersion version, OperationTransformer transformer) { createChildRegistry(address.iterator(), version, PathAddressTransformer.DEFAULT, RESOURCE_TRANSFORMER, new OperationTransformerRegistry.OperationTransformerEntry(transformer, false), false); } public void createChildRegistry(final PathAddress address, final ModelVersion version, ResourceTransformer resourceTransformer, boolean inherited) { createChildRegistry(address.iterator(), version, PathAddressTransformer.DEFAULT, new OperationTransformerRegistry.ResourceTransformerEntry(resourceTransformer, inherited), OperationTransformerRegistry.FORWARD, false); } public void createChildRegistry(final PathAddress address, final ModelVersion version, ResourceTransformer resourceTransformer, OperationTransformer operationTransformer, boolean placeholder) { createChildRegistry(address, version, PathAddressTransformer.DEFAULT, resourceTransformer, operationTransformer, placeholder); } public void createChildRegistry(final PathAddress address, final ModelVersion version, PathAddressTransformer pathAddressTransformer, ResourceTransformer resourceTransformer, OperationTransformer operationTransformer, boolean placeholder) { createChildRegistry(address.iterator(), version, pathAddressTransformer, new OperationTransformerRegistry.ResourceTransformerEntry(resourceTransformer, false), new OperationTransformerRegistry.OperationTransformerEntry(operationTransformer, false), placeholder); } /** * Register an operation transformer. * * @param address the transformer address * @param version the model version * @param pathAddressTransformer the path address transformer * @param resourceTransformer the resource transformer * @param operationTransformer the operation transformer * @param inherited whether the transformers are inherited * @param placeholder if {@code true} the pathAddress-, resource-, and operationTransformers are responsible for handling children of their address via a {@link org.jboss.as.controller.registry.OperationTransformerRegistry.PlaceholderResolver} */ public void createChildRegistry(final PathAddress address, final ModelVersion version, PathAddressTransformer pathAddressTransformer, ResourceTransformer resourceTransformer, OperationTransformer operationTransformer, boolean inherited, boolean placeholder) { createChildRegistry(address.iterator(), version, pathAddressTransformer, new OperationTransformerRegistry.ResourceTransformerEntry(resourceTransformer, false), new OperationTransformerRegistry.OperationTransformerEntry(operationTransformer, inherited), placeholder); } /** * Register an operation transformer. * * @param address the operation handler address * @param version the model version * @param operationName the operation name * @param transformer the operation transformer */ public void registerTransformer(final PathAddress address, final ModelVersion version, String operationName, OperationTransformer transformer) { registerTransformer(address.iterator(), version, operationName, new OperationTransformerRegistry.OperationTransformerEntry(transformer, false)); } public OperationTransformerRegistry mergeSubtree(final OperationTransformerRegistry parent, final PathAddress address, final Map<PathAddress, ModelVersion> subTree) { final OperationTransformerRegistry target = parent.createChildRegistry(address.iterator(), PathAddressTransformer.DEFAULT, RESOURCE_TRANSFORMER, OperationTransformerRegistry.FORWARD, false); mergeSubtree(target, subTree); return target; } /** * Merge a subtree. * * @param targetRegistry the target registry * @param subTree the subtree */ public void mergeSubtree(final OperationTransformerRegistry targetRegistry, final Map<PathAddress, ModelVersion> subTree) { for(Map.Entry<PathAddress, ModelVersion> entry: subTree.entrySet()) { mergeSubtree(targetRegistry, entry.getKey(), entry.getValue()); } } protected void mergeSubtree(final OperationTransformerRegistry targetRegistry, final PathAddress address, final ModelVersion version) { final GlobalTransformerRegistry child = navigate(address.iterator()); if(child != null) { child.process(targetRegistry, address, version, Collections.<PathAddress, ModelVersion>emptyMap()); } } public OperationTransformerRegistry create(final ModelVersion version, final Map<PathAddress, ModelVersion> versions) { final OperationTransformerRegistry registry = new OperationTransformerRegistry(PathAddressTransformer.DEFAULT, RESOURCE_TRANSFORMER, null, false); process(registry, PathAddress.EMPTY_ADDRESS, version, versions); return registry; } private void process(final OperationTransformerRegistry registry, final PathAddress address, final ModelVersion version, Map<PathAddress, ModelVersion> versions) { final OperationTransformerRegistry current = getRegistryUpdater(version); if(current != null) { final OperationTransformerRegistry.ResourceTransformerEntry resourceTransformer = current.getResourceTransformer(); final OperationTransformerRegistry.OperationTransformerEntry defaultTransformer = current.getDefaultTransformer(); registry.createChildRegistry(address.iterator(), current.getPathAddressTransformer(), resourceTransformer, defaultTransformer, current.isPlaceholder()); final Map<String, OperationTransformerRegistry.OperationTransformerEntry> transformers = current.getTransformers(); for(final Map.Entry<String, OperationTransformerRegistry.OperationTransformerEntry> transformer : transformers.entrySet()) { registry.registerTransformer(address, transformer.getKey(), transformer.getValue().getTransformer()); } } final Map<String, SubRegistry> snapshot = subRegistriesUpdater.get(this); if(snapshot != null) { for(final Map.Entry<String, SubRegistry> registryEntry : snapshot.entrySet()) { // final String key = registryEntry.getKey(); final SubRegistry subRegistry = registryEntry.getValue(); final Map<String, GlobalTransformerRegistry> children = SubRegistry.childrenUpdater.get(subRegistry); for(final Map.Entry<String, GlobalTransformerRegistry> childEntry : children.entrySet()) { // final String value = childEntry.getKey(); final GlobalTransformerRegistry child = childEntry.getValue(); final PathAddress childAddress = address.append(PathElement.pathElement(key, value)); final ModelVersion childVersion = versions.containsKey(childAddress) ? versions.get(childAddress) : version; child.process(registry, childAddress, childVersion, versions); } } } } private OperationTransformerRegistry getRegistryUpdater(final ModelVersion version) { int micro = version.getMicro(); for (int i = micro; i >= 0; i--) { ModelVersion currentVersion = ModelVersion.create(version.getMajor(), version.getMinor(), i); OperationTransformerRegistry current = registryUpdater.get(this, currentVersion); if (current != null) { if(micro != i && this.getClass().desiredAssertionStatus()) { ControllerLogger.MGMT_OP_LOGGER.couldNotFindTransformerRegistryFallingBack(version, currentVersion); } return current; } } return null; } private void createChildRegistry(final Iterator<PathElement> iterator, ModelVersion version, PathAddressTransformer pathAddressTransformer, OperationTransformerRegistry.ResourceTransformerEntry resourceTransformer, OperationTransformerRegistry.OperationTransformerEntry entry, boolean placeholder) { if(! iterator.hasNext()) { getOrCreate(version, pathAddressTransformer, resourceTransformer, entry, placeholder); } else { final PathElement element = iterator.next(); getOrCreate(element.getKey()).getOrCreate(element.getValue()).createChildRegistry(iterator, version, pathAddressTransformer, resourceTransformer, entry, placeholder); } } private void registerTransformer(final Iterator<PathElement> iterator, ModelVersion version, String operationName, OperationTransformerRegistry.OperationTransformerEntry entry) { if(! iterator.hasNext()) { // by default skip the default transformer getOrCreate(version, PathAddressTransformer.DEFAULT, null, null, false).registerTransformer(PathAddress.EMPTY_ADDRESS.iterator(), operationName, entry); } else { final PathElement element = iterator.next(); final SubRegistry subRegistry = getOrCreate(element.getKey()); subRegistry.registerTransformer(iterator, element.getValue(), version, operationName, entry); } } protected OperationTransformerRegistry.OperationTransformerEntry resolveTransformer(final Iterator<PathElement> iterator, ModelVersion version, String operationName) { if(!iterator.hasNext()) { final OperationTransformerRegistry registry = registryUpdater.get(this, version); if(registry == null) { return null; } return registry.resolveOperationTransformer(PathAddress.EMPTY_ADDRESS, operationName, null); } else { final PathElement element = iterator.next(); final SubRegistry registry = subRegistriesUpdater.get(this, element.getKey()); if(registry == null) { return null; } return registry.resolveTransformer(iterator, element.getValue(), version, operationName); } } private GlobalTransformerRegistry navigate(final Iterator<PathElement> iterator) { if(! iterator.hasNext()) { return this; } else { final PathElement element = iterator.next(); final SubRegistry registry = subRegistriesUpdater.get(this, element.getKey()); if(registry == null) { return null; } GlobalTransformerRegistry other = SubRegistry.childrenUpdater.get(registry, element.getValue()); if(other != null) { return other.navigate(iterator); } 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; } } } } private OperationTransformerRegistry getOrCreate(final ModelVersion version, PathAddressTransformer pathAddressTransformer, OperationTransformerRegistry.ResourceTransformerEntry resourceTransformer, final OperationTransformerRegistry.OperationTransformerEntry defaultTransformer, boolean placeholder) { for(;;) { final Map<ModelVersion, OperationTransformerRegistry> snapshot = registryUpdater.get(this); OperationTransformerRegistry registry = snapshot.get(version); if(registry != null) { return registry; } else { registry = new OperationTransformerRegistry(pathAddressTransformer, resourceTransformer, defaultTransformer, placeholder); if (registryUpdater.putAtomic(this, version, registry, snapshot)) { return registry; } } } } private static class SubRegistry { private static final AtomicMapFieldUpdater<SubRegistry, String, GlobalTransformerRegistry> childrenUpdater = AtomicMapFieldUpdater.newMapUpdater(AtomicReferenceFieldUpdater.newUpdater(SubRegistry.class, Map.class, "children")); private volatile Map<String, GlobalTransformerRegistry> children; private SubRegistry() { childrenUpdater.clear(this); } GlobalTransformerRegistry getOrCreate(final String value) { for(;;) { final Map<String, GlobalTransformerRegistry> entries = childrenUpdater.get(this); GlobalTransformerRegistry entry = entries.get(value); if(entry != null) { return entry; } else { entry = new GlobalTransformerRegistry(); if (childrenUpdater.putAtomic(this, value, entry, entries)) { return entry; } } } } public OperationTransformerRegistry.OperationTransformerEntry resolveTransformer(Iterator<PathElement> iterator, String value, ModelVersion version, String operationName) { final GlobalTransformerRegistry registry = childrenUpdater.get(this, value); if(registry == null) { return null; } return registry.resolveTransformer(iterator, version, operationName); } public void registerTransformer(Iterator<PathElement> iterator, String value, ModelVersion version, String operationName, OperationTransformerRegistry.OperationTransformerEntry entry) { getOrCreate(value).registerTransformer(iterator, version, operationName, entry); } } static OperationTransformerRegistry.ResourceTransformerEntry RESOURCE_TRANSFORMER = new OperationTransformerRegistry.ResourceTransformerEntry(ResourceTransformer.DEFAULT, false); static OperationTransformerRegistry.ResourceTransformerEntry DISCARD = new OperationTransformerRegistry.ResourceTransformerEntry(ResourceTransformer.DISCARD, true); }