/* * JBoss, Home of Professional Open Source. * Copyright 2014, 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.description; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.jboss.as.controller.OperationFailedException; import org.jboss.as.controller.PathAddress; import org.jboss.as.controller.PathElement; import org.jboss.as.controller.registry.Resource; import org.jboss.as.controller.registry.Resource.ResourceEntry; import org.jboss.as.controller.transform.ChainedTransformationTools; 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.ResourceTransformationContext; import org.jboss.as.controller.transform.ResourceTransformer; import org.jboss.as.controller.transform.TransformationContext; import org.jboss.as.controller.transform.description.ChainedTransformationDescriptionBuilderImpl.ModelVersionPair; import org.jboss.dmr.ModelNode; /** * Placeholder transformer implementation for chained transformers. It uses {@link org.jboss.as.controller.registry.OperationTransformerRegistry.PlaceholderResolver} to override how the transformers for the child resources * of the placeholder are resolved. * * * @author <a href="kabir.khan@jboss.com">Kabir Khan</a> */ class ChainedTransformingDescription extends AbstractDescription implements TransformationDescription, ResourceTransformer, OperationTransformer { private final LinkedHashMap<ModelVersionPair, ChainedPlaceholderResolver> placeholderResolvers; ChainedTransformingDescription(PathElement pathElement, LinkedHashMap<ModelVersionPair, ChainedPlaceholderResolver> placeholderResolvers) { super(pathElement, PathAddressTransformer.DEFAULT, true); this.placeholderResolvers = placeholderResolvers; } @Override public TransformedOperation transformOperation(TransformationContext context, PathAddress address, ModelNode operation) throws OperationFailedException { assert context instanceof ResourceTransformationContext : "Wrong context type"; ResourceTransformationContext currentCtx = (ResourceTransformationContext)context; List<TransformedOperation> delegates = new ArrayList<TransformedOperation>(); ModelNode currentOp = operation; Iterator<Map.Entry<ModelVersionPair, ChainedPlaceholderResolver>> it = placeholderResolvers.entrySet().iterator(); if (it.hasNext()) { ChainedPlaceholderResolver resolver = it.next().getValue(); currentCtx = ChainedTransformationTools.initialiseChain(currentCtx, resolver); PathAddress addr = ChainedTransformationTools.transformAddress(PathAddress.pathAddress(currentOp.require(OP_ADDR)), currentCtx.getTarget()); currentOp.get(OP_ADDR).set(addr.toModelNode()); OperationTransformer transformer = currentCtx.getTarget().resolveTransformer(currentCtx, address, currentOp.require(OP).asString()); TransformedOperation transformed = transformer.transformOperation(currentCtx, address, currentOp); currentOp = transformed.getTransformedOperation(); delegates.add(transformed); } while (it.hasNext()) { if (currentOp == null) { break; } ChainedPlaceholderResolver resolver = it.next().getValue(); currentCtx = ChainedTransformationTools.nextInChainOperation(currentCtx, resolver); PathAddress currentAddress = PathAddress.pathAddress(currentOp.require(OP_ADDR)); PathAddress addr = ChainedTransformationTools.transformAddress(currentAddress, currentCtx.getTarget()); currentOp.get(OP_ADDR).set(addr.toModelNode()); OperationTransformer transformer = currentCtx.getTarget().resolveTransformer(currentCtx, currentAddress, currentOp.require(OP).asString()); TransformedOperation transformed = transformer.transformOperation(currentCtx, address, currentOp); currentOp = transformed.getTransformedOperation(); delegates.add(transformed); } return new ChainedTransformedOperation(currentOp, delegates.toArray(new TransformedOperation[delegates.size()])); } @Override public void transformResource(final ResourceTransformationContext context, final PathAddress address, final Resource resource) throws OperationFailedException { if (resource.isProxy() || resource.isRuntime()) { return; } //For now just assume we come in through the top layer ResourceTransformationContext current = context; Iterator<Map.Entry<ModelVersionPair, ChainedPlaceholderResolver>> it = placeholderResolvers.entrySet().iterator(); if (it.hasNext()) { ChainedPlaceholderResolver resolver = it.next().getValue(); current = ChainedTransformationTools.initialiseChain(current, resolver); resolver.getDescription().getResourceTransformer().transformResource(current, address, resource); } while (it.hasNext()) { ChainedPlaceholderResolver resolver = it.next().getValue(); current = ChainedTransformationTools.nextInChainResource(current, resolver); try { Resource currentResource = current.readResourceFromRoot(address); resolver.getDescription().getResourceTransformer().transformResource(current, address, currentResource); } catch (Resource.NoSuchResourceException e) { //The resource was rejected/discarded continue; } } Resource transformed = current.getTransformedRoot(); Resource originalTransformed = context.getTransformedRoot(); copy(transformed, originalTransformed, address); } @Override public PathAddressTransformer getPathAddressTransformer() { return PathAddressTransformer.DEFAULT; } @Override public OperationTransformer getOperationTransformer() { return this; } @Override public ResourceTransformer getResourceTransformer() { return this; } @Override public Map<String, OperationTransformer> getOperationTransformers() { return Collections.emptyMap(); } @Override public List<TransformationDescription> getChildren() { return Collections.emptyList(); } @Override public Set<String> getDiscardedOperations() { return Collections.emptySet(); } @Override public boolean isPlaceHolder() { return true; } private void copy(Resource src, Resource dest, PathAddress address) { PathAddress parentAddress = address.size() > 1 ? address.subAddress(0, address.size() - 1) : PathAddress.EMPTY_ADDRESS; PathElement childElement = address.getLastElement(); Resource source = src.navigate(parentAddress); Resource destination = dest.navigate(parentAddress); Resource sourceChild = source.getChild(childElement); if (sourceChild != null) { Resource destChild = Resource.Factory.create(); destination.registerChild(childElement, destChild); copy(sourceChild, destChild); } //copy(src, dest); } private void copy(Resource src, Resource dest) { dest.getModel().set(src.getModel()); for (String type : src.getChildTypes()) { for (ResourceEntry entry : src.getChildren(type)) { Resource added = Resource.Factory.create(); dest.registerChild(PathElement.pathElement(type, entry.getName()), added); copy(entry, added); } } } private PathAddress transformAddress(final PathAddress original, final ResourceTransformationContext context) { return ChainedTransformationTools.transformAddress(original, context.getTarget()); } private static class ChainedTransformedOperation extends TransformedOperation { private TransformedOperation[] delegates; private volatile String failure; private volatile boolean initialized; public ChainedTransformedOperation(ModelNode transformedOperation, TransformedOperation...delegates) { // FIXME ChainedTransformedOperation constructor super(transformedOperation, null); this.delegates = delegates; } @Override public ModelNode getTransformedOperation() { return super.getTransformedOperation(); } @Override public OperationResultTransformer getResultTransformer() { return this; } @Override public boolean rejectOperation(ModelNode preparedResult) { for (TransformedOperation delegate : delegates) { if (delegate.rejectOperation(preparedResult)) { failure = delegate.getFailureDescription(); initialized = true; //See comment in getFailureDescription() return true; } } return false; } @Override public String getFailureDescription() { //In real life this will always be initialized by the transforming proxy before anyone calls this method //For testing we call it directly from ModelTestUtils if (!initialized) { for (TransformedOperation delegate : delegates) { String failure = delegate.getFailureDescription(); if (failure != null) { return failure; } } } return failure; } @Override public ModelNode transformResult(ModelNode result) { ModelNode currentResult = result; for (int i = delegates.length - 1 ; i >= 0 ; --i) { currentResult = delegates[i].transformResult(currentResult); } return currentResult; } } }