/*
* 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 org.jboss.as.controller.ExpressionResolver;
import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.ProcessType;
import org.jboss.as.controller.RunningMode;
import org.jboss.as.controller.registry.ImmutableManagementResourceRegistration;
import org.jboss.as.controller.registry.Resource;
import org.jboss.dmr.ModelNode;
/**
* Transformers API for manipulating transformation operations between different versions of application server
*
* @author Emanuel Muckenhuber
* @author Tomaz Cerar
* @since 7.1.2
*/
public interface Transformers {
/**
* Get information about the target.
*
* @return the target
*/
TransformationTarget getTarget();
/**
* <strong>Only for use by test frameworks.</strong> Transforms an operation.
*
* @param context contextual information about the transformation
* @param operation the operation to transform
* @return the transformed operation
* @throws OperationFailedException
*/
OperationTransformer.TransformedOperation transformOperation(TransformationContext context, ModelNode operation) throws OperationFailedException;
/**
* Transform an operation.
*
* @param transformationInputs standard inputs into a transformation process. Cannot be {@code null}
* @param operation the operation to transform. Cannot be {@code null}
* @return the transformed operation. Will not be {@code null}
* @throws OperationFailedException
*/
OperationTransformer.TransformedOperation transformOperation(TransformationInputs transformationInputs, ModelNode operation) throws OperationFailedException;
/**
* <strong>Only for use by test frameworks.</strong>. Transforms the given resource.
*
* @param context contextual information about the transformation
* @param resource to transform
* @return transformed resource, or same if no transformation was needed
* @throws OperationFailedException
*/
Resource transformResource(ResourceTransformationContext context, Resource resource) throws OperationFailedException;
/**
* Transform a given root resource, including children. The given {@code resource} must represent the root of
* HC's full resource tree but need not include all children, if the caller is not interested in transforming
* the excluded children.
*
* @param transformationInputs standard inputs into a transformation process. Cannot be {@code null}
* @param resource the root resource. Cannot be {@code null}
* @return the transformed resource. Will not be {@code null}
* @throws OperationFailedException
*/
Resource transformRootResource(TransformationInputs transformationInputs, Resource resource) throws OperationFailedException;
/**
* Transform a given resource, including children, removing resources that the given {@code ignoredTransformationRegistry}
* indicates are being ignored by the target process. The given {@code resource} must represent the root of
* HC's full resource tree but need not include all children, if the caller is not interested in transforming
* the excluded children.
*
* @param transformationInputs standard inputs to a transformation. Cannot be {@code null}
* @param resource the resource to be transformed (including children)
* @param ignoredTransformationRegistry provider of information on what addresses are being ignored by the target process
* @return the transformed resource
*
* @throws OperationFailedException
*/
Resource transformRootResource(TransformationInputs transformationInputs, Resource resource, ResourceIgnoredTransformationRegistry ignoredTransformationRegistry) throws OperationFailedException;
/**
* Standard inputs into a transformation process. These are derived from an {@link OperationContext}
* at the time they are created but this class does not use the operation context thereafter, making
* it safe for use by other threads not associated with the operation context.
*/
class TransformationInputs {
private static final OperationContext.AttachmentKey<TransformationInputs> KEY = OperationContext.AttachmentKey.create(TransformationInputs.class);
private final Resource originalModel;
private final ImmutableManagementResourceRegistration registration;
private final ProcessType processType;
private final RunningMode runningMode;
private final TransformerOperationAttachment transformerOperationAttachment;
/**
* Obtains a set of {@code TransformationInputs} from the given operation context. If the
* context's {@link OperationContext#getCurrentStage() current stage} is
* {@link org.jboss.as.controller.OperationContext.Stage#DOMAIN} any inputs cached with
* the context as an attachment will be used, and if none are cached, then the created inputs
* will be cached.
*
* @param context the operation context. Cannot be {@code null}
* @return the inputs. Will not be {@code null}
*/
public static TransformationInputs getOrCreate(OperationContext context) {
TransformationInputs result;
if (context.getCurrentStage() == OperationContext.Stage.DOMAIN) {
// Stage.DOMAIN means the model is not going to change, so we can safely cache.
// We want to cache because reading the entire model is expensive, so we don't
// want to do it for every process we need to interact with in a domain roll out.
result = context.getAttachment(KEY);
if (result == null) {
result = new TransformationInputs(context);
context.attach(KEY, result);
}
} else {
result = new TransformationInputs(context);
}
return result;
}
/**
* Creates a new {@code TransformationInputs} from the given operation context.
* @param context the operation context. Cannot be {@code null}
*/
public TransformationInputs(OperationContext context) {
this.originalModel = context.readResourceFromRoot(PathAddress.EMPTY_ADDRESS, true);
this.registration = context.getRootResourceRegistration();
this.processType = context.getProcessType();
this.runningMode = context.getRunningMode();
this.transformerOperationAttachment = context.getAttachment(TransformerOperationAttachment.KEY);
}
/**
* Gets a copy of the full resource tree as it existed at the time this object was created.
*
* @return the resource tree. Will not be {@code null}
*/
public Resource getRootResource() {
return originalModel;
}
/**
* Gets full the {@link ImmutableManagementResourceRegistration resource registration} tree.
* @return the resource registration tree. Will not be {@code null}
*/
public ImmutableManagementResourceRegistration getRootRegistration() {
return registration;
}
/**
* Gets the type of this process.
* @return the process type. Will not be {@code null}
*/
public ProcessType getProcessType() {
return processType;
}
/**
* Gets the process' running mode at the time this object was created.
* @return the running mode. Will not be {@code null}
*/
public RunningMode getRunningMode() {
return runningMode;
}
/**
* Gets any {@link TransformerOperationAttachment} that was attached to the {@link OperationContext}
* at the time this object was created.
* @return the attachment, or {@code null} if there was none.
*/
public TransformerOperationAttachment getTransformerOperationAttachment() {
return transformerOperationAttachment;
}
}
/**
* Convenience factory for unit tests, and default internal implementations
*/
class Factory {
private Factory() {
}
/**
* Returns a transformers object appropriate for the given target process.
* @param target the transformation target
* @return the transformers instance. Will not be {@code null}
*/
public static Transformers create(final TransformationTarget target) {
return new TransformersImpl(target);
}
/**
* Creates a ResourceTransformationContext
*
* @param target the transformation target
* @param model the model
* @param registration the resource registration
* @param resolver the expression resolver
* @param runningMode the server running mode
* @param type the process type
* @param attachment attachments propagated from the operation context to the created transformer context.
* This may be {@code null}. In a non-test scenario, this will be added by operation handlers
* triggering the transformation, but for tests this needs to be hard-coded. Tests will need to
* ensure themselves that the relevant attachments get set.
*
* @return the created context Will not be {@code null}
*/
public static ResourceTransformationContext create(TransformationTarget target, Resource model,
ImmutableManagementResourceRegistration registration, ExpressionResolver resolver,
RunningMode runningMode, ProcessType type, TransformerOperationAttachment attachment) {
return ResourceTransformationContextImpl.create(target, model, registration, runningMode, type, attachment, DEFAULT);
}
/**
* Creates a ResourceTransformationContext
*
* @param target the transformation target
* @param model the model
* @param registration the resource registration
* @param resolver the expression resolver
* @param runningMode the server running mode
* @param type the process type
* @param attachment attachments propagated from the operation context to the created transformer context.
* This may be {@code null}. In a non-test scenario, this will be added by operation handlers
* triggering the transformation, but for tests this needs to be hard-coded. Tests will need to
* ensure themselves that the relevant attachments get set.
*
* @return the created context Will not be {@code null}
*/
public static ResourceTransformationContext create(TransformationTarget target, Resource model,
ImmutableManagementResourceRegistration registration, ExpressionResolver resolver,
RunningMode runningMode, ProcessType type, TransformerOperationAttachment attachment,
Transformers.ResourceIgnoredTransformationRegistry ignoredTransformationRegistry) {
return ResourceTransformationContextImpl.create(target, model, registration, runningMode, type, attachment, ignoredTransformationRegistry);
}
/**
* Create a local transformer, which will use the default transformation rules, however still respect the
* ignored resource transformation.
*
* @return the transformers instance. Will not be {@code null}
*/
public static Transformers createLocal() {
return new TransformersImpl(TransformationTargetImpl.createLocal());
}
}
/** Provides information on whether a target process is ignoring particular resource addresses. */
@FunctionalInterface
interface ResourceIgnoredTransformationRegistry {
/**
* Gets whether a resource with the given {@code address} should be excluded from
* {@link TransformationTarget#resolveTransformer(ResourceTransformationContext, org.jboss.as.controller.PathAddress) resource transformation}.
*
* @param address the resource address. Cannot be {@code null}
* @return {@code true} if the resource should be excluded from resource transformation
*/
boolean isResourceTransformationIgnored(final PathAddress address);
}
/**
* A default {@link org.jboss.as.controller.transform.Transformers.ResourceIgnoredTransformationRegistry}
* that says that no addresses are being ignored.
*/
ResourceIgnoredTransformationRegistry DEFAULT = new ResourceIgnoredTransformationRegistry() {
/**
* Always returns {@code false}
*
* {@inheritDoc}
*
* @return {@code false}, always
*/
@Override
public boolean isResourceTransformationIgnored(PathAddress address) {
return false;
}
};
/** Provides information on whether a target process is excluded from receiving operations for a particular resource addresses. */
@FunctionalInterface
interface OperationExcludedTransformationRegistry {
/**
* Gets whether an operation with the given {@code address} should be excluded from normal
* {@link TransformationTarget#resolveTransformer(TransformationContext, PathAddress, String)} transformation}
* and instead simply {@link OperationTransformer#DISCARD discarded}.
*
* @param address the operation address. Cannot be {@code null}
* @param operationName the name of the operation
* @return {@code true} if the operation should be excluded from normal operation transformation
*/
boolean isOperationExcluded(final PathAddress address, String operationName);
/**
* A default {@link OperationExcludedTransformationRegistry}
* that says that no addresses are being excluded.
*/
OperationExcludedTransformationRegistry DEFAULT = new OperationExcludedTransformationRegistry() {
/**
* Always returns {@code false}
*
* {@inheritDoc}
*
* @return {@code false}, always
*/
@Override
public boolean isOperationExcluded(PathAddress address, String operationName) {
return false;
}
};
}
}