/* * 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.transform; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.jboss.as.controller.ModelVersion; import org.jboss.as.controller.ModelVersionRange; import org.jboss.as.controller.PathAddress; import org.jboss.as.controller.PathElement; import org.jboss.as.controller.descriptions.ModelDescriptionConstants; import org.jboss.as.controller.extension.ExtensionRegistry; import org.jboss.as.controller.logging.ControllerLogger; import org.jboss.as.controller.registry.GlobalTransformerRegistry; import org.jboss.as.controller.registry.OperationTransformerRegistry; import org.jboss.dmr.ModelNode; import org.jboss.dmr.Property; import org.jboss.modules.Module; import org.jboss.modules.ModuleIdentifier; import org.jboss.modules.ModuleLoadException; /** * Global transformers registry. * * @author <a href="mailto:tomaz.cerar@redhat.com">Tomaz Cerar</a> * @author Emanuel Muckenhuber */ public final class TransformerRegistry { public static final ModelNode DISCARD_OPERATION = new ModelNode(); static { DISCARD_OPERATION.get(OP).set("discard"); DISCARD_OPERATION.get(OP_ADDR).setEmptyList(); DISCARD_OPERATION.protect(); } private static final PathElement HOST = PathElement.pathElement(ModelDescriptionConstants.HOST); private static final PathElement PROFILE = PathElement.pathElement(ModelDescriptionConstants.PROFILE); private static final PathElement SERVER = PathElement.pathElement(ModelDescriptionConstants.RUNNING_SERVER); private final GlobalTransformerRegistry domain = new GlobalTransformerRegistry(); private final GlobalTransformerRegistry subsystem = new GlobalTransformerRegistry(); TransformerRegistry() { // Initialize the empty paths domain.createChildRegistry(PathAddress.pathAddress(PROFILE), ModelVersion.create(0), ResourceTransformer.DEFAULT, false); domain.createChildRegistry(PathAddress.pathAddress(HOST), ModelVersion.create(0), ResourceTransformer.DEFAULT, false); domain.createChildRegistry(PathAddress.pathAddress(HOST, SERVER), ModelVersion.create(0), ResourceTransformer.DEFAULT, false); } public void loadAndRegisterTransformers(String name, ModelVersion subsystemVersion, String extensionModuleName) { try { SubsystemTransformerRegistration transformerRegistration = new SubsystemTransformerRegistrationImpl(name, subsystemVersion); if (Module.getCallerModule() != null) { //only register when running in modular environment, testsuite does its own loading for (ExtensionTransformerRegistration registration : Module.loadServiceFromCallerModuleLoader(ModuleIdentifier.fromString(extensionModuleName), ExtensionTransformerRegistration.class)) { if (registration.getSubsystemName().equals(name)) { //to prevent registering transformers for different subsystems registration.registerTransformers(transformerRegistration); } } } } catch (ModuleLoadException e) { throw ControllerLogger.ROOT_LOGGER.couldNotLoadModuleForTransformers(extensionModuleName, e); } } public SubsystemTransformerRegistration createSubsystemTransformerRegistration(String name, ModelVersion currentVersion){ return new SubsystemTransformerRegistrationImpl(name, currentVersion); } private class SubsystemTransformerRegistrationImpl implements SubsystemTransformerRegistration{ private final String name; private final ModelVersion currentVersion; public SubsystemTransformerRegistrationImpl(String name, ModelVersion currentVersion) { this.name = name; this.currentVersion = currentVersion; } @Override public TransformersSubRegistration registerModelTransformers(final ModelVersionRange range, final ResourceTransformer subsystemTransformer) { return registerSubsystemTransformers(name, range, subsystemTransformer); } @Override public TransformersSubRegistration registerModelTransformers(ModelVersionRange version, ResourceTransformer resourceTransformer, OperationTransformer operationTransformer, boolean placeholder) { return registerSubsystemTransformers(name, version, resourceTransformer, operationTransformer, placeholder); } @Override public TransformersSubRegistration registerModelTransformers(ModelVersionRange version, CombinedTransformer combinedTransformer) { return registerSubsystemTransformers(name, version, combinedTransformer); } @Override public ModelVersion getCurrentSubsystemVersion() { return currentVersion; } } /** * Register a subsystem transformer. * * @param name the subsystem name * @param range the version range * @param subsystemTransformer the resource transformer * @return the sub registry */ public TransformersSubRegistration registerSubsystemTransformers(final String name, final ModelVersionRange range, final ResourceTransformer subsystemTransformer) { return registerSubsystemTransformers(name, range, subsystemTransformer, OperationTransformer.DEFAULT, false); } /** * Register a subsystem transformer. * * @param name the subsystem name * @param range the version range * @param subsystemTransformer the resource transformer * @param operationTransformer the operation transformer * @param placeholder whether or not the registered transformers are placeholders * @return the sub registry */ public TransformersSubRegistration registerSubsystemTransformers(final String name, final ModelVersionRange range, final ResourceTransformer subsystemTransformer, final OperationTransformer operationTransformer, boolean placeholder) { final PathAddress subsystemAddress = PathAddress.EMPTY_ADDRESS.append(PathElement.pathElement(SUBSYSTEM, name)); for(final ModelVersion version : range.getVersions()) { subsystem.createChildRegistry(subsystemAddress, version, subsystemTransformer, operationTransformer, placeholder); } return new TransformersSubRegistrationImpl(range, subsystem, subsystemAddress); } /** * Get the sub registry for the domain. * * @param range the version range * @return the sub registry */ public TransformersSubRegistration getDomainRegistration(final ModelVersionRange range) { final PathAddress address = PathAddress.EMPTY_ADDRESS; return new TransformersSubRegistrationImpl(range, domain, address); } /** * Get the sub registry for the hosts. * * @param range the version range * @return the sub registry */ public TransformersSubRegistration getHostRegistration(final ModelVersionRange range) { final PathAddress address = PathAddress.EMPTY_ADDRESS.append(HOST); return new TransformersSubRegistrationImpl(range, domain, address); } /** * Get the sub registry for the servers. * * @param range the version range * @return the sub registry */ public TransformersSubRegistration getServerRegistration(final ModelVersionRange range) { final PathAddress address = PathAddress.EMPTY_ADDRESS.append(HOST, SERVER); return new TransformersSubRegistrationImpl(range, domain, address); } /** * Resolve the host registry. * * @param mgmtVersion the mgmt version * @param subsystems the subsystems * @return the transformer registry */ public OperationTransformerRegistry resolveHost(final ModelVersion mgmtVersion, final ModelNode subsystems) { return resolveHost(mgmtVersion, resolveVersions(subsystems)); } /** * Resolve the host registry. * * @param mgmtVersion the mgmt version * @param subsystems the subsystems * @return the transformer registry */ public OperationTransformerRegistry resolveHost(final ModelVersion mgmtVersion, final Map<PathAddress, ModelVersion> subsystems) { // The domain / host / servers final OperationTransformerRegistry root = domain.create(mgmtVersion, Collections.<PathAddress, ModelVersion>emptyMap()); subsystem.mergeSubtree(root, PathAddress.pathAddress(PROFILE), subsystems); subsystem.mergeSubtree(root, PathAddress.pathAddress(HOST, SERVER), subsystems); return root; } /** * Resolve the server registry. * * @param mgmtVersion the mgmt version * @param subsystems the subsystems * @return the transformer registry */ public OperationTransformerRegistry resolveServer(final ModelVersion mgmtVersion, final ModelNode subsystems) { return resolveServer(mgmtVersion, resolveVersions(subsystems)); } /** * Resolve the server registry. * * @param mgmtVersion the mgmt version * @param subsystems the subsystems * @return the transformer registry */ public OperationTransformerRegistry resolveServer(final ModelVersion mgmtVersion, final Map<PathAddress, ModelVersion> subsystems) { // this might not be all that useful after all, since the operation to remote servers go through the host proxies anyway final OperationTransformerRegistry root = domain.create(mgmtVersion, Collections.<PathAddress, ModelVersion>emptyMap()); return subsystem.mergeSubtree(root, PathAddress.pathAddress(HOST, SERVER), subsystems); } /** * Add a new subsystem to a given registry. * * @param registry the registry * @param name the subsystem name * @param version the version */ void addSubsystem(final OperationTransformerRegistry registry, final String name, final ModelVersion version) { final OperationTransformerRegistry profile = registry.getChild(PathAddress.pathAddress(PROFILE)); final OperationTransformerRegistry server = registry.getChild(PathAddress.pathAddress(HOST, SERVER)); final PathAddress address = PathAddress.pathAddress(PathElement.pathElement(ModelDescriptionConstants.SUBSYSTEM, name)); subsystem.mergeSubtree(profile, Collections.singletonMap(address, version)); if(server != null) { subsystem.mergeSubtree(server, Collections.singletonMap(address, version)); } } public static Map<PathAddress, ModelVersion> resolveVersions(ExtensionRegistry extensionRegistry) { final ModelNode subsystems = new ModelNode(); for (final String extension : extensionRegistry.getExtensionModuleNames()) { extensionRegistry.recordSubsystemVersions(extension, subsystems); } return resolveVersions(subsystems); } public static Map<PathAddress, ModelVersion> resolveVersions(final ModelNode subsystems) { final PathAddress base = PathAddress.EMPTY_ADDRESS; final Map<PathAddress, ModelVersion> versions = new HashMap<PathAddress, ModelVersion>(); for(final Property property : subsystems.asPropertyList()) { final String name = property.getName(); final PathAddress address = base.append(PathElement.pathElement(ModelDescriptionConstants.SUBSYSTEM, name)); versions.put(address, ModelVersion.fromString(property.getValue().asString())); } return versions; } static ModelVersion convert(final String version) { final String[] s = version.split("\\."); final int length = s.length; if(length > 3) { throw new IllegalStateException(); } int major = Integer.valueOf(s[0]); int minor = length > 1 ? Integer.valueOf(s[1]) : 0; int micro = length == 3 ? Integer.valueOf(s[2]) : 0; return ModelVersion.create(major, minor, micro); } public static class Factory { /** * Create a new Transformer registry. * * @return the created transformer registry */ public static TransformerRegistry create() { return new TransformerRegistry(); } } public static class TransformersSubRegistrationImpl implements TransformersSubRegistration { private final PathAddress current; private final ModelVersionRange range; private final GlobalTransformerRegistry registry; public TransformersSubRegistrationImpl(ModelVersionRange range, GlobalTransformerRegistry registry, PathAddress parent) { this.range = range; this.registry = registry; this.current = parent; } @Override public TransformersSubRegistration registerSubResource(PathElement element) { return registerSubResource(element, ResourceTransformer.DEFAULT, OperationTransformer.DEFAULT); } @Override public TransformersSubRegistration registerSubResource(PathElement element, boolean discard) { if(discard) { final PathAddress address = current.append(element); for(final ModelVersion version : range.getVersions()) { registry.createDiscardingChildRegistry(address, version); } return new TransformersSubRegistrationImpl(range, registry, address); } return registerSubResource(element, ResourceTransformer.DEFAULT, OperationTransformer.DEFAULT); } @Override public TransformersSubRegistration registerSubResource(PathElement element, OperationTransformer operationTransformer) { return registerSubResource(element, ResourceTransformer.DEFAULT, operationTransformer); } @Override public TransformersSubRegistration registerSubResource(PathElement element, ResourceTransformer resourceTransformer) { return registerSubResource(element, resourceTransformer, OperationTransformer.DEFAULT); } @Override public TransformersSubRegistration registerSubResource(PathElement element, CombinedTransformer transformer) { return registerSubResource(element, transformer, transformer); } @Override public TransformersSubRegistration registerSubResource(PathElement element, ResourceTransformer resourceTransformer, OperationTransformer operationTransformer) { return registerSubResource(element, PathAddressTransformer.DEFAULT, resourceTransformer, operationTransformer); } @Override public TransformersSubRegistration registerSubResource(PathElement element, PathAddressTransformer pathAddressTransformer, ResourceTransformer resourceTransformer, OperationTransformer operationTransformer) { return registerSubResource(element, pathAddressTransformer, resourceTransformer, operationTransformer, false, false); } @Override public TransformersSubRegistration registerSubResource(PathElement element, PathAddressTransformer pathAddressTransformer, ResourceTransformer resourceTransformer, OperationTransformer operationTransformer, boolean inherited, boolean placeholder) { final PathAddress address = current.append(element); for(final ModelVersion version : range.getVersions()) { registry.createChildRegistry(address, version, pathAddressTransformer, resourceTransformer, operationTransformer, inherited, placeholder); } return new TransformersSubRegistrationImpl(range, registry, address); } @Override public void discardOperations(String... operationNames) { for(final ModelVersion version : range.getVersions()) { for(final String operationName : operationNames) { registry.discardOperation(current, version, operationName); } } } @Override public void registerOperationTransformer(String operationName, OperationTransformer transformer) { for(final ModelVersion version : range.getVersions()) { registry.registerTransformer(current, version, operationName, transformer); } } } }